🛣️

[Next.js]Next.js のRouting処理 Dynamic Parallel and Intercepted Routesについ

2024/09/11に公開

前書き

なぜこの記事を書いたのか

最近、Next.js(14+)を学びながらデモプロジェクトを作成しています。ファイルシステムベースのルーターは私にとって新しいものです。基本的な概念は簡単に理解できましたが、Parallel RoutesやIntercepting Routesなどの高度な機能を理解するのに少し時間がかかりました。同じような疑問を持つ人がいるかもしれないと思い、みんなの助けになればと思ってこの記事を書きました。

Next.jsがファイスシステムベースのルーターを使用していることすでに知っていると仮定して、基本的な部分は省略し、Dynamic Routesのセクションから始めます。

Dynamic Routes

Dynamic Routesとは

Dynamic Routesは、URLの異なるパラメータに適用できるページ作成することを可能にします。単一の動的ルートを使用して、複数のシナリオを処理できます。

Dynamic Routesの作成

Dynamic Routesを作成するのはとても簡単で、ファイル名に角括弧を追加するだけです。例えば:

app/post/[slug]/page.tsx

これはブログ投稿用の動的ルートを作成し、[slug]にはどのような値を入れることができます。

ルーターパラメータへのアクセス

ページコンポーネント内で、paramsを使用してルートの動的な部分にアクセスできます。以下は簡単な例です:

// app\post\[slug]\page.tsx
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await getBlogPost(params.slug)
  return (
    <article>
      <h1>{post.title}</h1>
      <div>{{ post.content }}</div>
    </article>
  )
}

Catch-All Routes と Optional Catch-All Routes

先ほど作成したの動的ルートは[foldername]という形式で命名され、ページコンポーネントで一つのパラメータを受け取ります。しかし、複数の動的ルートパラメータが必要な場合はどうでしょうか?例えば、app/shop/[category]/[id]/page.jsのような構造の場合。このような場合にCatch-All RoutesとOptional Catch-All Routes役立ちます。

Catch-All Routes

Next.jsは[...folderName]という形式でCatch-All Routesを提供します。これにより、複数のパスセグメントを捕捉することができます。例えば:

app/shop/[...slug]/page.js

このルートは以下のようなパスにマッチします:

  • /shop/clothes

  • /shop/clothes/shirts

  • /shop/clothes/shirts/summer

// app\dynamic-routes\catch-all\[...slug]\page.tsx
export default function DRCatchAllPage({ params }: { params: { slug: string[] } }) {
    const { slug } = params
    return (
        <div className="text-center">
            Current Page is Dynamic Route Page with Catch-all Segments.

            {/* slug will be an array: ['clothes', 'shirts', 'summer'] */}
            <h1>Category: {slug.join(' > ')}</h1>
        </div>
    )
}
Catch-All Routes画面デモ

Optional Catch-All Routes

二重の角括弧を追加することで、Catch-All Routesはオプショナルにすることができます:

app/shop/[[...slug]]/page.js

このルートは以下のようなパスにマッチします:

  • /shop

  • /shop/clothes

  • /shop/clothes/shirts/summer

    通常のCatch-All Routesとの違いは、このルートはパラメータなしでも動けます。

Optional Catch-All Routes画面デモ(パスありの場合)

Optional Catch-All Routes画面デモ(パスなしの場合)

// app\dynamic-routes\optional-catch-all\shop\[[...slug]]\page.tsx

export default function DROptCatchAllPage({ params }: { params: { slug?: string[] } }) {
    return (
        <div className="text-center">
            Current Page is Dynamic Route Page with Optional Catch-all Segments.
            <br />
            The param info is: {params.slug ? params.slug.join('>') : 'empty info'}
        </div>
    )
}

Intercepting Routes

時には、ユーザーが特定のページに到達する前に、一時停止してリダイレクトしたいことがあります。Next.jsはこれを実現するためにIntercepting Routesを提供します。

Intercepting Routesとは

Intercepting Routesは、通常表示されるコンポーネントやページとは異なるものを表示するために、ルートを「intercept」する機能を提供します。

Intercepting Routesの作成

app/
├── intercepting/
│   └── [id]/
│   │   └── page.tsx
│   └── page.tsx
└── @modal/
    └── (.)intercepting/
        └── [id]/
        │   └── page.tsx
        └── default.tsx

この構造では、(.)interceptingがIntercepting Routeです。(.)は「同じセグメントのルートをインターセプトする」ことを意味します。

インターセプションの種類

Next.jsは異なる種類のインターセプションを提供しています:

  1. (.) - 同じセグメントをインターセプト
  2. (..) - 親セグメントをインターセプト
  3. (...) - すべてのセグメントをインターセプト
Intercepting Routes動作



Intercepting Routesの動作

  1. クリック時の挙動:
    ユーザーが画面上のアイテムをクリックすると、URLは変更されますが、現在の画面はそのまま維持されます。その上で、新しいコンポーネントが表示されます。
  2. URL変更:
    URLは即座に変更され、ターゲットページのURLになります。しかし、画面全体は更新されません。
  3. 画面の状態:
    元の画面は背景に残り、その上に新しいコンポーネントが表示されます。これにより、ユーザーは元のコンテスト失うことなく、詳細情報を見ることや別の操作を行うのができます。
  4. リフレッシュ時の挙動:
    ユーザーがページをリフレッシュすると、現在のURLに対応する完全なページが表示されます。

## Parallel Routes

キッチンで料理を作っているを想像して、コンロでパスタを茹でながら、オーブンでパンを焼きています。これがNext.jsのParallel Routesのようなものです。複数のことを同時に進行していますが、すべて同じ食事(この場合は画面ページ)の一部なのです。

Parallel Routesとは

Next.jsのParallel Routesは、同じレイアウト内で複数のページやコンポネントを同時にレンダリングすることが可能にします。

Parallel Routesの作成

Parallel Routesを作成するには、フォルダ名の前に@記号を追加します。例えば:

parallel-routes/
│── layout.tsx
│── @pasta/
│   └── page.tsx
│── @bread/
    └── page.tsx

この設定により、@pasta@breadの両方のコンポネントを同じレイアウト内で並べて表示できます。

注意:@で始まるフォルダ名はslatsと呼ばれ、page.tsxファイルが含まれていてもルートとして認識されません。

Parallel Routesを実現するには、フォルダ名に@記号を使用するだけでなく、layout.tsxファイルでこれらをchildrenと同様にパラメータとして受け取ります。

export default function ParallelRoutesLayout({
  children,
  pasta,
  bread,
}: {
  children: React.ReactNode
  bread: React.ReactNode
  pasta: React.ReactNode
}) {
  return (
    <>
      {children}
      <div className="flex text-center">
        <div className="w-1/2 p-4">
          <h1 className="text-2xl font-bold mb-4">pasta Side</h1>
          {pasta}
        </div>
        <div className="w-1/2 p-4">
          <h1 className="text-2xl font-bold mb-4">bread Side</h1>
          {bread}
        </div>
      </div>
    </>
  )
}

Parallel Routesと通常のコンポネントは多くの共通点がありますが、最大の違いは、Parallel Routesにはルートのような機能が備わっていることだと思います。例えば:ローディング状態、エラー処理、デフォルトのフォールバックなどがあります。これらは通常のコンポネントでは手動で実装する必要があります。

Regular Parallel Routes

default.tsxについて
特定スロットにマッチするルートがない場合、フォールバックとして機能します。
各スロット内で必要があれば、独自のdefault.tsxを持ちます。

parallel-routes/
│── layout.tsx
│── price/
│   └── page.tsx
│── @pasta/
│   └── page.tsx
│   └── default.tsx
│── @bread/
    └── page.tsx
    └── price/
        └── page.tsx

この構造にように、スロット間は独立性があり、独自のルーティング構造を持ちます。
/parallel-routes/priceに移動した場合:

  • @breadスロットにはpriceルートが存在するため、コンテンツが正常に表示されます。
  • @priceスロットにはルートが存在しないため、default.tsxが必要になります。
Parallel Routes with default info

おわりに

ここまでNext.jsのいろんなルーティングの種類について、ざっくりと説明してきました。
何か気になることがあれば、気軽にコメントしてくださいね!

参考

https://nextjs.org/docs/app/building-your-application/routing
https://zenn.dev/chot/articles/88ee3dc4697e57

Discussion