🛣️

【Next.js】Next.js の画面遷移を実装してみる

2024/01/24に公開

初めに

今回は Next.js で作ったアプリケーションで画面遷移ができるように実装してみたいと思います。
Next.js における next/link next/router などを使って以下を実装します。

  • 次の画面への遷移
  • 次の画面への値渡し
  • 前の画面への遷移
  • id などに基づいた動的なルーティング

なお、Next.js に関しては歴が非常に浅いため、誤り等あれば指摘していただければ幸いです。

記事の対象者

  • Next.js 初学者
  • Next.js の基本的な画面遷移を実装したい方

使用技術

  • Next.js : 13.5.6

目的

今回は先述の通り Next.js で基本的なページの画面遷移を実装することを目的とします。
今までは特に Flutter を用いたモバイル開発に携わることが多かったので、その辺りの違いなども共有できればと思います。

実装

実装は以下の手順で進めていきたいと思います。

  1. 次の画面への遷移
  2. 前の画面への遷移
  3. id などに基づいた動的なルーティング
  4. 次の画面への値渡し

なお、今回は Next のプロジェクト作成は完了している段階からスタートします。

1. 次の画面への遷移

この章では Firstページから Secondページに遷移する実装を行います。
最もシンプルな例として以下のようにします。

pages/first.tsx
import Link from "next/link";

function First() {
  return (
    <div>
      <h1>First</h1>
      <Link href={"/second"}>Second へ</Link>
    </div>
  );
}

export default First;
pages/second/index.tsx
function Second() {
  return <div>Second</div>;
}

export default Second;

画面遷移を行う際は Link を使用します。
Linkhref にルートを指定することで、特定のページに遷移することができます。
ここで指定するルートはプロジェクトのディレクトリ構造、ファイル名に依存します。

Firstコンポーネントを含む first.tsxpages/first.tsxに配置します。
Secondコンポーネントを含む index.tsxpages/second/index.tsxに配置します。

pagesディレクトリや index.jsindex.tsx というファイル名は Next.js のプロジェクト内では特別な意味を持ちます。
例えば、pages/hoge/index.tsx というディレクトリ構造にした場合、Linkhref/hoge を指定することで、hoge ディレクトリに含まれる index.tsx に記述されているコンポーネントが表示されます。

先述のコードでは、これらの仕組みを利用して href/second を指定することで index.tsx に記述されている Secondコンポーネントを表示させています。

実際にコードを実行すると以下のように正常に画面遷移ができてURLも更新されていることがわかります。

2. 前の画面への遷移

次に前の画面へ遷移する「戻るボタン」を実装してみたいと思います。
First から Second に遷移した後に First に戻る処理を行います。

pages/second/index.tsx を以下のように変更します。

pages/second/index.tsx
import { useRouter } from "next/router";

function Second() {
  const router = useRouter();
  return (
    <div>
      <h1>Second</h1>
      <button
        onClick={() => {
          router.back();
        }}
      >
        戻る
      </button>
    </div>
  );
}

export default Second;

button を押した際の処理として、router.back() を実行しています。
routeruseRouter を用いたものであり、useRouter はルーティングを行うための hook です。詳しくは Next.js公式ドキュメント をご覧ください。

実行してみると以下のように Second に進んだ後、 「戻るボタン」を押すと First に戻ることができるようになっています。

useRouter から遡ると以下のようなコードがありました。
window.historyに関する記事によるとwindow.history は「ブラウザーのセッション履歴 (現在のページが読み込まれているタブまたはフレームで訪れたことがあるページ群)」とされており、back() はその履歴を一つ遡る操作であることがわかります。

class Router {
    reload() {
        window.location.reload();
    }
    /**
   * Go back in history
   */ back() {
        window.history.back();
    }
    /**
   * Go forward in history
   */ forward() {
        window.history.forward();
    }
Flutter との比較

少し Flutter の Navigator1.0 と比較してみると、 Flutter では Navigator.pop() のように記述し、それぞれのページを次々に垂直方向に積み重ねていき、前のページに戻る場合は現在のページを跳ね除けるイメージです。
一方で今回の Router ではユーザーが訪れたページを history として、back() forward() で水平方向に移動するイメージがあります。

3. id などに基づいた動的なルーティング

まずは secondディレクトリに [id].tsx という名前のファイルを作成します。
そして中身を以下のようにします。

second/[id].tsx
export default function SecondDetail() {
  return <h1>Second Detail</h1>;
}

次に Firstコンポーネントを以下のように変更します。

pages/first.tsx
import Link from "next/link";

function First() {
  const id = "hoge";
  return (
    <div>
      <h1>First</h1>
      <p>
        <Link href={"/second"}>Second へ</Link>
      </p>
      <p>
        <Link href={`/second/${id}`}>Second Detail へ</Link>
      </p>
    </div>
  );
}

export default First;

実行してみると以下のように正常に画面遷移し、URLは http://localhost:3000/second/hoge のように id で指定した値である hoge が入っていることがわかります。

Linkhref でディレクトリ名の後に、作成したファイル名と同じ名前の変数を渡すと、その変数に入っている値がルートとなるページを表示させることができます。
なお、Linkhref に関して、href={`/scd/${id}`} のようにディレクトリ名を間違えた場合は動作しなくなるので、タイプミスには注意が必要です。

4. 次の画面への値渡し

先程の実装では id を用いた動的な遷移を実装しました。しかし、次のページに値を渡すところまではできていなかったので、単にURLが変更されただけでした。この章では、次のページに値を渡して、ページごとに異なる内容を表示させてみます。
まずは First を以下のように変更します。

pages/first.tsx
import Link from "next/link";

const skills = [
  { id: "1", name: "Dart", proficiency: 5 },
  { id: "2", name: "Swift", proficiency: 4 },
  { id: "3", name: "TypeScript", proficiency: 3 },
  { id: "4", name: "Python", proficiency: 3 },
];

function First() {
  return (
    <div>
      <h1>First</h1>
      {skills.map((skill) => (
        <p>
          <Link
            href={{
              pathname: `/second/${skill.id}`,
              query: {
                id: skill.id,
                name: skill.name,
                proficiency: skill.proficiency,
              },
            }}
          >
            {skill.name}
          </Link>
        </p>
      ))}
    </div>
  );
}

export default First;

この変更では、skills として各スキルの id, name, proficiency を保持するデータを用意します。実際のユースケースではデータベースから取得してきたJSON形式のデータなどが考えられます。
次に登録した skills の数だけリンクを作成し、 pathname: `/second/${skill.id}`, とすることで各スキルに登録されている id に応じた遷移を行います。

そして、以下のように query としてそれぞれの値を割り当てて、次のページに値を渡すことができます。

pages/first.tsx
query: {
  id: skill.id,
  name: skill.name,
  proficiency: skill.proficiency,
},

次に First から値を受け取る SecondDetail に関して、以下のように変更します。

pages/second/[id].tsx
import { useRouter } from "next/router";

export default function SecondDetail() {
  const router = useRouter();
  return (
    <div>
      <h1>{router.query.name}</h1>
      <p>{`熟練度: ${router.query.proficiency}`}</p>
    </div>
  );
}

先程の章でも使った useRouter() を用いて、渡された値を受け取ることができます。
router.query で、前のページから渡された query にアクセスすることができ、それぞれのスキルの値を引き出して表示させることができます。

実行してみると以下のように各ページごとのURLになり、値も次のページに渡せていることがわかります。
また、URLも各スキルに応じて変更されていることがわかります。
これで値渡しは完了です。

まとめ

最後まで読んでいただいてありがとうございました。
今回は Next.js の画面遷移についてまとめました。
今回紹介した機能以外にも、さまざまなオプションがあるので、今後触れてみたいと思います。

誤っている点や他の実装方法等あればご指摘いただけると幸いです。

参考

Routing
https://nextjs-ja-translation-docs.vercel.app/docs/routing/introduction

useRouter
https://nextjs.org/docs/pages/api-reference/functions/use-router

window.history
https://developer.mozilla.org/ja/docs/Web/API/Window/history

Discussion