2020-12-15に更新

Next.jsのDynamic RoutesでLinkを使うとSPAにならない件について

追記)Next.js10からはこの as の件は必要なくなり、勝手に認識してくれるようになりました。

Next.jsにはDynamic Routesという機能があり、 posts/[id].js のようなファイル名でページを作ると posts/12 のように動的なURLでアクセスできるページにすることができる。

しかし、このページに対してNext.jsの Link を使って <Link href="/posts/12"> のようにして遷移してみると、どうもSPAとしてアクセスできるのではなく、通常のリンクとしてのアクセスになってしまい、画面全体が読み込まれてSPAにならない(Next.js9.3現在)。

ではどうするのかというと下記のように as を利用する。

<Link href="/posts/[id]" as="/posts/12">

つまり、実在する [id] ページにアクセスするが、見た目上はasのURLにアクセスさせる、という意味。ドキュメントにも一応書かれてはいた。

as - The path that will be rendered in the browser URL bar. Used for dynamic routes

なんとなく、サーバーサイド、クライアント側のWebフレームワーク、例えばNuxt.jsにしろLaravelなどにしろ、作ったURLにそのままアクセスすればなんとなくいい感じにルーティングしてくれるのでその感覚でいると悩むことになる。Next.jsはちゃんと実際のURLと見た目上のURLを両方指定しなければいけない。恐らくNext.js的にも「そんなURLないけど?」という感じで単なるリンクにしてしまうのだろう。

パラメータとstateを利用する場合の問題

これはこれで良いとして、useStateを使っている場合は別の問題が発生する。例えば、動的URLのパラメータを取得するのには下記のようにrouterを利用する。

  const router = useRouter()
  return <div>{router.query.id}</div>

これは別に良いのだが、違うパラメータの同じページ、例えば posts/11 から posts/12 にアクセスする場合、この間にuseStateが混ざっているとうまくいかなくなる。

  const router = useRouter()
  const [post, setPost] = useState(getPost(router.query.id))
  return <div>{post.name}</div>

画面遷移後にrouterの値自体はもちろんちゃんと変わるのだが、useStateから取得している値は変わらない。もちろんこれはsetしていないから。元々は完全なページ切り替わりによって全てがリセットされていたのでちゃんと全てが新しいデータに切り替わっていたが、ちゃんとSPAできるようになると今度はルーティングのパラメータ以外は何も変わらなくなり、両方の値を使って表示をしているとなんかぐちゃぐちゃになる。

ということで、ルーティングのパラメータが変わった場合は、また改めて手動で値をセットし直さなければならない。具体的にはuseEffectを使って下記のようにする。

  const router = useRouter()
  const [post, setPost] = useState(getPost(router.query.id))

  useEffect(() => {
    setPost(getPost(router.query.id))
  }, [router.query.id])

  return <div>{post.name}</div>

このようにして、idが変わったらデータを取り直す、という処理を入れる。もしpostに紐づくcommentsも取得している、ということであればそちらなども全部取得してsetし直す必要がある。コンポーネントの最初で色々連動させてuseStateしているからといって勝手に連動してくれたりはしない。

ツイッターでシェア
みんなに共有、忘れないようにメモ

だら@Crieit開発者

Crieitの開発者です。 Webエンジニアです(在宅)。大体10年ちょい。 記事でわかりにくいところがあればDMで質問していただくか、案件発注してください。 業務依頼、同業種の方からのコンタクトなどお気軽にご連絡ください。 業務経験有:PHP, MySQL, Laravel, React, Flutter, Vue.js, Node, RoR 趣味:Elixir, Phoenix, Nuxt, Express, GCP, AWS等色々 PHPフレームワークちいたんの作者

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント