Linking and Navigating
Next.jsルーターは、シングルページのアプリケーションと同じように、
クライアントサイドでページ間のルート遷移を行うことができます。
このクライアント側のルート遷移を行うために、
LinkというReactコンポーネントが用意されている。
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About Us</Link>
</li>
<li>
<Link href="/blog/hello-world">Blog Post</Link>
</li>
</ul>
)
}
export default Home
上の例では複数のリンクを使用している。
それぞれが既知のページへのパス(href)をマッピングしている。
- / → pages/index.js
- /about → pages/about.js
- /blog/hello-world → pages/blog/[slug].js
静的生成(Static Generation)を使用しているページでは、
(初期状態またはスクロールによって)ビューポートにあるすべての<Link />が、
デフォルトでプリフェッチされます(対応するデータを含む)。
サーバレンダリングされたルートの対応するデータは、
<Link />がクリックされたときのみフェッチされます。
Linking to dynamic paths
動的なルートセグメントで便利な補間を使ってパスを作ることもできる。
たとえば、propとしてコンポーネントに渡された投稿のリストを表示。
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
または、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オブジェクトを使う。
- pathnameはpagesディレクトリ内のページ名
- この場合、/blog/[slug]
- queryはダイナミックセグメントを持つオブジェクト
Injecting the router
サンプルコード: https://github.com/vercel/next.js/tree/canary/examples/dynamic-routing
Reactコンポーネントでrouter objectにアクセスするには、
useRouterまたはwithRouterを使用する。
一般的にはuseRouter をお薦めする。
Imperative Routing
next/linkはルーティングのほとんどのニーズをカバーできるはずだが、
next/routerを使わなくてもクライアントサイドのナビゲーションを行うこともできる。
次の例では、useRouterを使って基本的なページ・ナビゲーションを行う方法を示している。
import { useRouter } from 'next/router'
export default function ReadMore() {
const router = useRouter()
return (
<button onClick={() => router.push('/about')}>
Click here to read more
</button>
)
}
Shallow Routing
サンプルコード: https://github.com/vercel/next.js/tree/canary/examples/with-shallow-routing
シャロールーティングでは、
データ取得メソッドを再度実行することなくURLを変更することができる。
このメソッドには、getServerSideProps、getStaticProps、getInitialPropsが含まれる。
状態を失うことなく、更新されたpathnameとqueryをrouter object(useRouterまたはwithRouterで追加)を介して受け取ることができる。
シャロー・ルーティングを有効にするには、
shallowオプションをtrueに設定する。
import { useEffect } from 'react'
import { useRouter } from 'next/router'
// Current URL is '/'
function Page() {
const router = useRouter()
useEffect(() => {
// Always do navigations after the first render
router.push('/?counter=10', undefined, { shallow: true })
}, [])
useEffect(() => {
// The counter changed!
}, [router.query.counter])
}
export default Page
URLは/?counter=10に更新され、ページは置き換えられず、ルートの状態だけが変更される。
以下のように、componentDidUpdateを介してURLの変更を監視することもできる。
componentDidUpdate(prevProps) {
const { pathname, query } = this.props.router
// verify props have changed to avoid an infinite loop
if (query.counter !== prevProps.router.query.counter) {
// fetch data based on the new query
}
}
Caveats
シャロー・ルーティングは、現在のページのURL変更に対してのみ機能する。
例えば、pages/about.jsという別のページがあり、これを実行したとする。
router.push('/?counter=10', '/about?counter=10', { shallow: true })
これは新しいページなので、現在のページをアンロードし、
新しいページをロードし、浅いルーティングを要求したにもかかわらず、
データのフェッチを待つ。
シャロー・ルーティングがミドルウェアとともに使用される場合、
以前ミドルウェアなしで行われたように、
新しいページが現在のページと一致することを保証しません。
これはミドルウェアが動的に書き換えられるためで、
シャローではスキップされるデータフェッチなしではクライアントサイドで検証できません。
つまり、
- シャロー・ルーティングではミドルウェアは実行されない
- シャロー・ルーティングを使う際は、ミドルウェアに依存しない前提で設計する
- ミドルウェアが必要なケースでは、シャロー・ルーティングを使わないのもあり