リンクとナビゲーション

Next.jsのルーターを使用すると、シングルページアプリケーション(SPA)と同様に、クライアントサイドでのページ間遷移を実現できます。

クライアントサイドのルート遷移を行うために、LinkというReactコンポーネントが提供されています。

import Link from 'next/link'

function Home() {
  return (
    <ul>
      <li>
        <Link href="/">ホーム</Link>
      </li>
      <li>
        <Link href="/about">会社概要</Link>
      </li>
      <li>
        <Link href="/blog/hello-world">ブログ記事</Link>
      </li>
    </ul>
  )
}

export default Home

上記の例では複数のリンクを使用しています。それぞれのリンクはパス(href)を既知のページにマッピングしています:

  • /pages/index.js
  • /aboutpages/about.js
  • /blog/hello-worldpages/blog/[slug].js

ビューポート内にあるすべての<Link />(初期表示時またはスクロールによって表示されたもの)は、静的生成(Static Generation)を使用するページに対して、デフォルトでプリフェッチされます(対応するデータを含む)。サーバーサイドレンダリング(SSR)を使用するルートの対応データは、<Link />がクリックされた時点でのみフェッチされます。

動的パスへのリンク

動的ルートセグメントに対しては、パスを作成するために文字列補間を使用することもできます。例えば、コンポーネントにプロップとして渡された投稿のリストを表示する場合:

import Link from 'next/link'

function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${encodeURIComponent(post.slug)}`}>
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Posts

この例では、パスをUTF-8互換に保つためにencodeURIComponentを使用しています。

別の方法として、URLオブジェクトを使用することもできます:

import Link from 'next/link'

function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link
            href={{
              pathname: '/blog/[slug]',
              query: { slug: post.slug },
            }}
          >
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}

export default Posts

この場合、パスを作成するために文字列補間を使用する代わりに、hrefにURLオブジェクトを使用しています:

  • pathnamepagesディレクトリ内のページ名です。この場合は/blog/[slug]
  • queryは動的セグメントを持つオブジェクトです。この場合はslug

ルーターの注入

Reactコンポーネント内でrouterオブジェクトにアクセスするには、useRouterまたはwithRouterを使用します。

一般的にはuseRouterの使用を推奨します。

命令的なルーティング

next/linkでほとんどのルーティングニーズを満たせますが、それを使用せずにクライアントサイドナビゲーションを行うこともできます。next/routerのドキュメントを参照してください。

以下の例は、useRouterを使用した基本的なページナビゲーションの方法を示しています:

import { useRouter } from 'next/router'

export default function ReadMore() {
  const router = useRouter()

  return (
    <button onClick={() => router.push('/about')}>
      詳細を読む
    </button>
  )
}

シャロー(浅い)ルーティング

シャロールーティングを使用すると、データ取得メソッド(getServerSidePropsgetStaticPropsgetInitialProps)を再実行せずにURLを変更できます。

useRouterまたはwithRouterによって追加されるrouterオブジェクトを通じて、状態を失うことなく更新されたpathnamequeryを受け取ります。

シャロールーティングを有効にするには、shallowオプションをtrueに設定します。以下の例を参照してください:

import { useEffect } from 'react'
import { useRouter } from 'next/router'

// 現在のURLは '/'
function Page() {
  const router = useRouter()

  useEffect(() => {
    // 初回レンダリング後にナビゲーションを実行
    router.push('/?counter=10', undefined, { shallow: true })
  }, [])

  useEffect(() => {
    // カウンターが変更されました!
  }, [router.query.counter])
}

export default Page

URLは/?counter=10に更新されますが、ページは置き換えられず、ルートの状態のみが変更されます。

URLの変更はcomponentDidUpdateでも監視できます:

componentDidUpdate(prevProps) {
  const { pathname, query } = this.props.router
  // 無限ループを避けるためにプロップが変更されたことを確認
  if (query.counter !== prevProps.router.query.counter) {
    // 新しいクエリに基づいてデータを取得
  }
}

注意点

シャロールーティングは現在のページのURL変更に対してのみ機能します。例えば、pages/about.jsという別のページがあり、次のコードを実行すると:

router.push('/?counter=10', '/about?counter=10', { shallow: true })

これは新しいページであるため、現在のページをアンロードし、新しいページをロードしてデータ取得を待機します(シャロールーティングを指定した場合でも)。

シャロールーティングがミドルウェアとともに使用される場合、ミドルウェアなしで以前に行われたように新しいページが現在のページと一致することを保証しません。これは、ミドルウェアが動的に書き換えることができ、データフェッチなしではクライアントサイドで検証できないためです(シャローではスキップされます)。したがって、シャロールート変更は常にシャローとして扱われる必要があります。