Yanonoblog!

こつこつと

Next.js - ダイナミックルーティング

背景

実務ではReactを使っているのですがNext.jsのキャッチアップしておきたいと思ったので、その内容を整理したいと思います。

本記事では、Next.jsに関する情報をまとめています。

内容は易しめです。

前回の続きになります!

Next.js - ルーティング / ページ遷移の基本

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

静的パスのルーティング

Next.jsでは、フォルダーとファイルの名前で自動的にパスが割り当てられます。

pages/blog/post.tsxというファイルを作成した場合、/blog/postにアクセスすることで、そのコンポーネントが表示されるようになります。

静的パスでは動的パスに対応できない

**/data/01とすると1番目のデータが表示される**といったようなページのURLにパラメータが含まれるような動的パスに対応するには、本来ならば「data」フォルダー内に「1.js」というファイルとして用意しないといけません。

ただ、上記のようなやり方ではアプリケーションが成り立ちませんね。

Next.jsでは動的パスに対応するためのダイナミックルーティングという機能が用いられます。

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

ダイナミックルーティングでは、以下でいう[post]のように配列の的な変数値を任意のファイル名で定義するイメージです。

pages/
  ├── index.tsx
  ├── about.tsx
  ├── [post].tsx

動的なURLにアクセスされたときに、[post].tsxファイルが呼び出されます。

このファイル内で、アクセスされたURLに含まれるパラメータを取得して、それに基づいてページをレンダリングすることができます。

使用してみる

Next.jsに用意されている「useRouter」という機能を使います。

import { useRouter } from 'next/router'

useRouterは、以下のようにしてrouterオブジェクトを作成して利用します。

const router = useRouter()

このrouterからパラメータの情報を取り出し利用します。

[]を使ってパラメータに設定された値は、router.queryというプロパティにまとめられています。

routerオブジェクト

routerオブジェクトの中身は以下のようになっていました。

Untitled

routerオブジェクトは、Next.jsアプリケーション内でのページ遷移やクエリパラメータの取得などに使われます。

以下は一部を抜粋した解説表になります。

名称 説明
pathname 現在のURLのパス部分を取得する。
query クエリパラメータを含むオブジェクトを取得する。
asPath ユーザーがアクセスしているURLを取得する。
push ページ遷移する。
replace 現在のページを別のページに置き換える。
back 前のページに戻る。
prefetch データを事前に取得し、キャッシュする。
isReady ページの読み込みが完了したかどうかを取得する。
route 現在のURLのパス部分を取得する。
basePath ベースパスを取得する。
isFallback フォールバックの状態を取得する。
router.(push / repace / reload) - ページ遷移の制御

push, replace, reloadメソッドを活用すると、ブラウザの履歴を操作しつつページ遷移を制御することができます。

ブラウザの履歴を操作しながら「次のページ」に遷移する例 - push

import { useRouter } from 'next/router'

function handleClick() {
  const router = useRouter()
  router.push('/next-page')
}

ブラウザの履歴を上書きして、前のページに戻れなくする例 - **replace**

import { useRouter } from 'next/router'

function handleClick() {
  const router = useRouter()
  router.replace('/next-page')
}

現在のページをリロードする例 - reload

import { useRouter } from 'next/router'

function handleClick() {
  const router = useRouter()
  router.reload()
}
router.isReady - 初期化の完了確認

Next.jsのuseRouterフックで取得できるrouterオブジェクトは、初期化が完了するまで情報が入っていないため、isReadyプロパティを使用して初期化が完了したことを確認する必要があります。

useEffect(()=>{
  if(!router.isReady) return
  
// router.queryから値を取得し処理を行う

}, [router.isReady]);

動的パスの実装例

動的なURLに遷移したときに対応するコンポーネントを実装した例を紹介します。

画面収録-2023-04-26-20 37 19

遷移元 - src/pages/**index.tsx**

下記のコードではAPIで取得したデータリストのそれぞれの要素に対応するデータの詳細ページへのリンクを「 show detail. 」を表示させます。

// src/pages/index.tsx

...略...

{sampleData.map((item, index) => (
  <div key={index} className="alert alert-info my-3">
    <p className="h5">msg: "{item.text}".</p>
    <p className="h6">by {item.author}. ({item.email})</p>
    <Link href={`/data/${index + 1}`} as={`/data/${index + 1}`} passHref>
      show detail.
    </Link>
  </div>
))}

...略...

リンクの URL は、 /data/[num]として指定されており、実際の URLは/data/1/data/2/data/3... となります。

遷移先 - src/pages/data/**[num].tsx**

このコードは/data/:numという動的パスのページを表示させるpageコンポーネントです。

このファイル自体が遷移先としてのページの内容を記述しています。

パス中の:numは、実際のアクセス時に指定された数値に置き換えられます。

fetchSampleDataを使用してデータを取得した後、現在のページへのアクセス情報とともに、対応するデータを選択します。

// src/pages/data/[num].tsx

import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import fetchSampleData from '../api/sample';

type Data = {
  text: string;
  author: string;
  email: string;
};

const DataDetail = () => {
  const [sampleData, setSampleData] = useState<Data[]>([]);
  const [selectedData, setSelectedData] = useState<Data | null>(null);
  const router = useRouter();
  const { num } = router.query;
  const [isLoading, setIsLoading] = useState(true); // ローディング状態を管理する

  useEffect(() => {
    const getData = async () => {
      const json = await fetchSampleData();
      setSampleData(json);
    };
    getData();
  }, []);

  useEffect(() => {
    if (num && sampleData.length > 0) {
      const index = Number(num) - 1;
      setSelectedData(sampleData[index]);
      setIsLoading(false); // データの読み込みが完了したらローディングを解除する
    }
  }, [num, sampleData]);

  if (isLoading) { // ローディング中はコンポーネントを表示しない
    return <div>Loading...</div>;
  }

  return (
    <main className="container">
      <h2 className="my-4">Data Detail page.</h2>
      {selectedData ? (
        <div className="alert alert-primary">
          <table className="table">
            <thead>
              <tr>
                <th>Text</th>
                <th>Author</th>
                <th>Email</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>{selectedData.text}</td>
                <td>{selectedData.author}</td>
                <td>{selectedData.email}</td>
              </tr>
            </tbody>
          </table>
        </div>
      ) : (
        <div className="alert alert-danger">No data found.</div>
      )}
    </main>
  );
};

export default DataDetail;

続く…

コメント

本記事の内容は以上になります!

続きのアウトプットも随時更新したいと思います。

間違いがありましたら修正いたしますので、ご指摘ください。

興味があれば他の記事も更新していきますので是非ご覧になってください♪


プログラミングスクールのご紹介 (卒業生より)

お世話になったプログラミングスクールであるRUNTEQです♪

https://runteq.jp/r/ohtFwbjW

こちらのリンクを経由すると1万円引きになります。

RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

https://twitter.com/outputky

参考