13
16

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 1 year has passed since last update.

【React / Next.js】ルーティング・遷移の基礎

Last updated at Posted at 2023-06-13

基本的なルーティング

■ pages配下が対象

基本的にはNext.jsのプロジェクトを作成したときにデフォルトで配置されている、
pagesディレクトリの配下を参照し、ルーティングされる。



スクリーンショット 2023-06-12 23.17.14.png



サーバを起動し、ローカルホストのリンク(http://localhost:3000など) を入力しリクエストを送れば、通常以下のようなページにアクセスできる。



スクリーンショット 2023-06-12 23.21.55.png



ここで、pages配下に一つフォルダを作成し、その中にindex.jsを作成する。(フォルダ名は任意)



【 ディレクトリ階層 】

root/
 ├ src/
 │ └ pages/
 │   └ sample/
 │    └ index.js

【 index.js 】

export default function Home() {
  return <h1>HOME</h1>;
}


URLのリンクを「 ローカルホスト / sample 」http://localhost:3000/sample) に変更し、リクエストを飛ばすと、以下のようなページが表示される。



スクリーンショット 2023-06-12 23.30.26.png



index.js以外のファイルを作成すると

先ほどのように、フォルダ直下にindex.jsを作成した場合は、

「リンク / フォルダ名」

でページにアクセスすることができる。



しかし、index.js以外の名前のファイルを作成した場合は、

「リンク / フォルダ名 / ファイル名」

とすることで、ページにアクセスすることができる。



【 ディレクトリ階層 】

root/
 ├ src/
 │ └ pages/
 │   └ sample/
 │    └ blog.js

【 blog.js 】

export default function Blog() {
  return <h1>Blog</h1>;
}


リクエスト結果は以下の通り。



スクリーンショット 2023-06-12 23.41.12.png



動的なルーティング(ダイナミックルーティング)

ダイナミックルーティングとは

URLの一部分を動的に変更する値にすることで、一つのファイルで複数のルーティングを可能にすること。
以下の例では、末尾の数字を動的に変更するダイナミックルーティングを作成する。

【 ディレクトリ階層 】

root/
 ├ src/
 │ └ pages/
 │   └ blog/
 │    └ [number].js

ダイナミックルーティングを作成するときは、ファイル名を[]で囲い[任意の名前].js とする必要がある。

こうすることで、[]内の名前(今回の例でいうnumber)がダイナミックルーティングとなり、jsファイル内で値として扱えるようになる。



【 [number].js 】

export default function Number() {
  return <h1>[number].js</h1>;
}


URLのリンクを「 ローカルホスト / blog / 1 」にし、リクエストを飛ばす。
結果は以下の通り。



スクリーンショット 2023-06-13 0.59.50.png



この時、末尾の1は好きな値に置き換えても同様のページにアクセスできる。



ダイナミックルーティングの値をjs内で取得する方法

取得する方法は以下の2通り。

① getServerSidePropsという関数を使用する。

② React HookのuseRouterを使用する。



① getServerSidePropsを使用する

getserverSidePropsメソッドから戻されたオブジェクトのpropsというプロパティに設定された値が、関数コンポーネントのプロップスとして渡ってくるという仕組み。

関数コンポーネントにpropsを渡してみる


【 ディレクトリ階層 】

root/
 ├ src/
 │ └ pages/
 │   └ blog/
 │    └ [number].js

【 [number].js 】

export default function Number({ hello }) {
  return <h1>{hello}.js</h1>;
}
export async function getServerSideProps() {
  return {
    props: { hello: "こんにちは" },
  };
}


リクエストを送信。結果は以下の通り。



スクリーンショット 2023-06-13 1.22.40.png



動的にpropsの値を取得する

動的に値を取得するには、getserverSidePropsメソッドの引数である、contextを利用する。

【 [number].js 】

export default function Number({ hello }) {
  return <h1>{hello}.js</h1>;
}
export async function getServerSideProps(context) {
  console.log(context);

  return {
    props: { hello: "こんにちは" },
  };
}


consoleの出力結果を確認すると大量のログが出てくるが、注目するべきなのは以下のログ。



{
  query: { number: '1' },
  resolvedUrl: '/07_router/blog/1',
  params: { number: '1' },
  locales: undefined,
  locale: undefined,
  defaultLocale: undefined
}


このqueryというところに、自分が入力した任意のパスの値が入っていることが分かる。

つまり、このcontext内のquery.numberにアクセスすれば、リクエスト時に入力した任意の値を動的に取得することが可能になる。

早速実践。



export default function Number({ query }) {
  return <h1>{query.number}.js</h1>;
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}



結果は以下の通り。



スクリーンショット 2023-06-13 2.00.07.png



先ほどのログでは、



query: { number: '1' },


となっていたが、オブジェクトのプロパティ名はダイナミックルーティングの[]内に記載した名前によって変わる。



② React HookのuseRouterを使用する

先ほどのgetServerSidePropsとは違うメソッドになるが、利用の仕方はほぼ同じ。

【 [number.js] 】

import { useRouter } from "next/router";

export default function Number({ query }) {
  const router = useRouter();

  console.log(router);

  return <h1>{router.query.number}.js</h1>;
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}


結果とログは以下の通り。



スクリーンショット 2023-06-13 2.25.51.png



ログ

ServerRouter {
  route: '/07_router/blog/[number]',
  pathname: '/07_router/blog/[number]',
  query: { number: '1' },
  asPath: '/07_router/blog/1',
  isFallback: false,
  basePath: '',
  locale: undefined,
  locales: undefined,
  defaultLocale: undefined,
  isReady: true,
  domainLocales: undefined,
  isPreview: false,
  isLocaleDomain: false
}


useRouterからクエリパラーメータを取得

【クエリパラメータ】
URLのリンクの?以降のkeyと値のセットのこと。

例)http://localhost:8080/sample/1?key=value

【 リクエストのリンク 】

スクリーンショット 2023-06-14 0.28.18.png



【 ログ 】

スクリーンショット 2023-06-14 0.27.33.png



useRouterを利用して画面遷移を行う

useRouterのpushメソッドを利用することで、画面遷移を行うことが可能。

import { useRouter } from "next/router";

export default function Number({ query }) {
  const router = useRouter();

  console.log(router);

  const clickHandler = () => {
    // トップページに戻る
    router.push("/");
  };
  return (
    <>
      <h1>{router.query.number}.js</h1>
      <button onClick={clickHandler}>アクションによる画面遷移</button>
    </>
  );
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}


【 画面 】

スクリーンショット 2023-06-14 0.37.36.png



【 ボタンクリック後 】

スクリーンショット 2023-06-14 0.39.05.png


補足

pushメソッドは、第二引数にダミーのURLを設定することで、第一引数のリンクに遷移しつつ、画面に表示されるリンクを第二引数にすることができる。

import { useRouter } from "next/router";

export default function Number({ query }) {
  const router = useRouter();

  console.log(router);

  const clickHandler = () => {
    // トップページに戻る
    router.push("/", "dummy-url");
  };
  return (
    <>
      <h1>{router.query.number}.js</h1>
      <button onClick={clickHandler}>アクションによる画面遷移</button>
    </>
  );
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}


【 画面(遷移前) 】

スクリーンショット 2023-06-14 0.43.42.png



【 遷移後 】

スクリーンショット 2023-06-14 0.45.31.png



遷移 + 履歴を上書き

pushメソッドと同様に画面遷移をすることに加え、履歴を上書きするメソッドとして、replaceメソッドが存在する。



解説(pushメソッド)

まずpushメソッドの場合の挙動確認。最初に以下のリンクにアクセス。



スクリーンショット 2023-06-14 1.08.16.png



 

ここから「アクションによる画面遷移」ボタンをクリックし、トップページに遷移する。



スクリーンショット 2023-06-14 0.45.31.png

このときにブラウザバック(左上の矢印の戻るボタン)ボタンをクリックすると、
先ほどの一番最初にリクエストで送信した画面に戻る。



スクリーンショット 2023-06-14 1.08.16.png



という挙動になる。pushメソッドはこれまでの履歴に1つ履歴を追加するというメソッドという認識が正しい。

googleトップページ → blog/1のページ → dummy-url(履歴を追加)

解説(replaceメソッド)

次にreplaceメソッドの挙動を確認。ソースは以下の通り。

import { useRouter } from "next/router";

export default function Number({ query }) {
  const router = useRouter();

  console.log(router);

  const clickHandler = () => {
    // トップページに戻る
    router.push("/", "dummy-url");
  };
  return (
    <>
      <h1>{router.query.number}.js</h1>
      <button onClick={clickHandler}>アクションによる画面遷移</button>
    </>
  );
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}


この状態で先ほどと同じ動作確認を実行。



スクリーンショット 2023-06-14 1.08.16.png



ボタンを押下し、画面遷移。



スクリーンショット 2023-06-14 0.45.31.png



そしてこの状態でブラウザバックボタンを押すと、リクエストを飛ばす前のgoogleのトップページへ遷移する。



スクリーンショット 2023-06-14 1.15.27.png



これがreplaceメソッドの上書きである。

googleトップページ → blog/1のページ → dummy-url(履歴を上書き)

フォルダもダイナミックルーティングできる

【 ディレクトリ階層 】

root/
 ├ src/
 │ └ pages/
 │   └ [name]/
 │    └ settings.js
 │   └ blog/
 │    └ [number].js

【 settings.js 】

export default function Settings() {
  return <h1>[name]/settings.js</h1>;
}

URLのパスを、「 ローカルホスト / A / settings 」にし、リクエストを飛ばす。
結果は以下の通り。



スクリーンショット 2023-06-13 0.28.59.png

もちろん、 / A / のところはどんな名前でもアクセス可能。



ダイナミック vs 固定のパス

root/
 ├ src/
 │ └ pages/
 │   └ [name]/
 │    └ settings.js
 │   └ blog/
 │    └ [number].js

上記のようなディレクトリ階層の時、
ローカルホスト / blog / settings
というリンクでリクエストを飛ばすとどうなるのかを検討する。

/blog/はblogフォルダはもちろんのこと、[name]フォルダも該当する。

/settings/も同様に、どちらのファイルにも該当する。

結果は以下の通り。



スクリーンショット 2023-06-13 0.37.37.png

結果は固定パスが優先

ダイナミックルーティングを行なっている同階層に同じ固定のパスがあり、そちらに一致する場合は、固定パスの方が優先されるということになる。



ダイナミックvsダイナミック

それでは、同階層にダイナミックルーティングが2つ以上あったらどうなるのか。

結論、これはエラーになる。
システム上、同階層に設定できるダイナミックルーティングは1つまでというルールがあるので、同階層に複数のダイナミックルーティングの設定はできない。



Linkコンポーネントを利用した画面遷移

Linkコンポーネントを使用して、画面遷移を行う方法も紹介。



import { useRouter } from "next/router";
import Link from "next/link";

export default function Number({ query }) {

  return (
    <>
      <Link href="../set/settings">
        <a>[name]/settingsに遷移</a>
      </Link>
    </>
  );
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}


【 画面 】

スクリーンショット 2023-06-14 1.54.35.png



【 リンク押下 】

スクリーンショット 2023-06-14 1.55.24.png



メリット

Linkコンポーネントを使用した遷移のメリットとしては、遷移するときに画面のリロードがされないので、画面遷移の高速化が期待できる。

試しにLinkコンポーネントと通常のaタグで遷移した場合の挙動確認を行う。



Linkコンポーネントの場合

背景を水色にし、Linkコンポーネント側のリンクで画面遷移をすると、

スクリーンショット 2023-06-14 2.00.48.png



スクリーンショット 2023-06-14 2.02.05.png



背景色が変わらないまま画面遷移ができる。



アンカータグの場合

同様の条件でaタグのリンクをクリックすると、



スクリーンショット 2023-06-14 2.03.49.png



画面がリロードされるため、ディベロッパーツールで設定したスタイルが初期化され、背景色が白色に戻っていることがわかる。



スクリーンショット 2023-06-14 2.04.29.png



アンカータグの画面遷移による弊害

アンカータグの繊維による弊害は、Linkコンポーネントと比較して低速というだけではない。

リロードを行うと、state(状態)で管理している値が初期化されてしまう。

以下の例は、ある画面で保持した値を別画面に共有して表示する例である。

スクリーンショット 2023-06-15 2.25.49.png



「リストページへ」ボタンを押下し、赤枠のデータを別画面に共有する。



スクリーンショット 2023-06-15 2.27.53.png



値が保持されていることが確認できる。
しかし、これをアンカータグで遷移すると、



スクリーンショット 2023-06-15 2.29.20.png



このように、リロードされたことによりstateの値が初期化されるので、遷移したときには何も表示されないという現象が起きる。



その他リンクの設定も可能

useRouterのpushメソッドやreplaceメソッドと同様、画面表示用のダミーURLを設定することが可能。

Linkコンポーネントにas属性を設定することで設定ができる。

import { useRouter } from "next/router";
import Link from "next/link";

export default function Number({ query }) {
  const router = useRouter();

  console.log(router);

  const clickHandler = () => {
    // トップページに戻る
    router.replace("/", "dummy-url");
  };
  return (
    <>
      <Link href="/" as="dummy-url">
        <a>トップ</a>
      </Link>
    </>
  );
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}

クエリパラメータの設定や、リンクをオブジェクト形式で記載することも可能。

import { useRouter } from "next/router";
import Link from "next/link";

export default function Number({ query }) {
  const router = useRouter();

  console.log(router);

  const clickHandler = () => {
    // トップページに戻る
    router.replace("/", "dummy-url");
  };
  return (
    <>
      <Link
        href={{
          // パスのオブジェクト設定
          pathname: "/",
          // クエリパラメータの設定
          query: { key: "value" },
        }}
        as="dummy-url"
      >
        <a>トップ</a>
      </Link>
    </>
  );
}
export async function getServerSideProps({ query }) {
  return {
    props: { query },
  };
}

スクリーンショット 2023-06-14 2.31.43.png

13
16
0

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
13
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?