105
101

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Next.jsのルーティング

Last updated at Posted at 2018-12-05

本記事は公式ドキュメントの翻訳を一部含みます
https://nextjs.org/docs/#routing

静的なルーティング

Next.jsはデフォルトで、ファイルシステムがルーティングとなります。例えば、pages以下に置かれたファイルがpages/about.jsだったとすると、localhost:3000/aboutにアクセスできるようになります。
そしてクライアントサイドのみの遷移を実現するために、Next.jsは<Link>というコンポーネントを用意してくれています。シンプルなルーティングでは、以下のようにhrefパラメータにhref="/about"と書けば遷移できます。

pages/index.js
import Link from 'next/link'

export default () => (
  <div>
    Click{' '}
    <Link href="/about">
      <a>here</a>
    </Link>{' '}
    to read more
  </div>
)

動的なルーティング

では/about/:nameのようなページがパラメータを持って変化するような、動的なページ遷移はどうしたらよいでしょう。

<Link>コンポーネントには、hrefの他にasというパラメータがあり、ブラウザヒストリーの書き換えはasに渡された文字列で行われます。
一方、どのページに、どんなパラメータで遷移するかをNext.js内部に知らせるには、下記サンプルのようなURLオブジェクトをhrefに渡します。
pathnameはpages以下のファイル、queryの値にはクエリのオブジェクトを渡します。

pages/index.js
import Link from 'next/link'

export default () => (
  <div>
    Click{' '}
    <Link
      as='/about/Zeit'
      href={{ pathname: '/about', query: { name: 'Zeit' } }}
    >
      <a>here</a>
    </Link>{' '}
    to read more
  </div>
)

URLオブジェクト

<Link>コンポーネントと同じように、Routerの関数にURLオブジェクトを渡すことで、履歴のpushreplaceが可能です。

import Router from 'next/router'

const handler = () => {
  Router.push({
    pathname: '/about',
    query: { name: 'Zeit' }
  })
}

export default () => (
  <div>
    Click <span onClick={handler}>here</span> to read more
  </div>
)

ルーターイベント

ルーター内部で実行されるイベントを捕捉したコールバックを作ることができます。
以下のイベントがサポートされています:

  • routeChangeStart(url) - ルートが変化を開始する際に発火
  • routeChangeComplete(url) - ルートの変化が完了した際に発火
  • routeChangeError(err, url) - ルートが変化する最中、エラーが起こった際に発火
  • beforeHistoryChange(url) - ブラウザ履歴が変化する直前に発火
  • hashChangeStart(url) - ページが遷移せず、ハッシュでの遷移(id付きのaタグでスクロールするなど)が開始する際に発火
  • hashChangeComplete(url) - ページが遷移せず、ハッシュでの遷移が完了した際に発火

routeChangeStartを使った例がこちらです。

const handleRouteChange = url => {
  console.log('App is changing to: ', url)
}

Router.events.on('routeChangeStart', handleRouteChange)

リアルワールドでのルーティング例

#1 ローディング中を示したい

SSRされたあとはSPAとしてページ遷移を行っているので、遷移中(ローディング中)ということをユーザーに示したいこともあります。
そういう場合はNProgressなどを導入しますが、これをどうやってNext.jsで実行するかが、こちらに例として載っています。
各イベントに対してNProgressのイベントをマッピングさせています。

pages/_app.js
Router.events.on('routeChangeStart', (url) => {
  console.log(`Loading: ${url}`)
  NProgress.start()
})
Router.events.on('routeChangeComplete', () => NProgress.done())
Router.events.on('routeChangeError', () => NProgress.done())

#2 オーバーレイコンポーネントが閉じないとき

SPAあるあるだと思いますが、ページ遷移時に前のコンポーネントの状態が残っていることがあると思います。例えば以下のケースです。

  • ハンバーガーメニューを押すとオーバーレイのコンポーネントが出てくる
  • メニュー内のリンクを押す
  • ページ遷移は起こるがオーバーレイのコンポーネントが閉じない

これを解決するのにはrouteChangeCompleteをイベントを利用すればよいでしょう。

Router.events.on('routeChangeComplete', () => closeMenu())

#3 記事中のaタグでもクライアントで移動したい

CMSからの記事をdangerouslySetInnerHTMLでそのまま出力するとき、クライアントサイドで遷移させたいaタグがあったとしても、そのまま<Link>コンポーネントを使用することは出来ないと思います。
そんなときは、componentDidMountライフサイクルなどで、ターゲットとなるaタグに上記のRouter.pushを使ってNext.jsのリンクを後付けすれば、クライアントサイドでの遷移が実現可能です。

#4 Next.js version6以前の場合、ルーティングイベントが重複して登録できない

Next.js version6以前だと、Router.event.onというイベントハンドラが無かったため、下記のようなutilを作成していました。今はversion7なので需要はないとは思いますが。

page-transition.js
import Router from "next/router";

export const hookOnRouterChangeStart = (fnc, that) => {
  const prev = Router.onRouteChangeStart;
  Router.onRouteChangeStart = async () => {
    if (typeof prev === "function") {
      prev.call(that);
    }
    fnc();
  };
};

おわりに

他にもNext.jsのルーティングは細かいところまでサポートしています。
例えば

  • <Link prefetch>によるページのプリフェッチ
  • scroll={false}によるスクロールイベントの中止
  • <Link>の子コンポーネントがaタグ以外の要素でのページ遷移
  • getInitialPropsが走らないで遷移をするShallow Routing
  • withRouterHOCによるrouterオブジェクトへのアクセス

などがあります。

詳しくはこちらをチェックして下さい。

105
101
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
105
101

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?