🔥

Parallel routesとSuspense

2024/01/15に公開

はじめに

Parallel routes、すごくいい機能だと思います。ただ、バグが多いということも聞きます。できるだけ新しい機能を使いたいと思い、採用していたのですが、バグっぽい挙動にぶち当たってしまい、今では必ずしも現段階で採用しなくてもいいのでは?と思っています。

本記事ではParallel routesの簡単な紹介と、コンポーネント毎のloadingの表示分けだけで利用するなら、少なくとも現時点では、suspenseでもいいのでは?ということを記載しようと思います。

補足

自分が当てたバグは、Parallel routesをRootLayout.tsxで利用している前提で、最初に'/'ページにアクセスして、それ以下のページにアクセスはできるのですが、例えば'/admin'ページにURL直打ちでアクセスした後、'/'ページに移動すると真っ白な画面しか表示されないというものでした。(Parallel routesをやめると治る)

parallel routes

簡単に言うと、コンポーネントを分割するための便利機能です。

https://nextjs.org/docs/app/building-your-application/routing/parallel-routes

機能概要

  • layout.tsx内で利用可能
  • テンプレートを作成できる
  • それぞれのコンポーネント毎にローディング画面やエラー画面など設定可能
  • 条件による出し分けが可能
  • ページごとに表示する内容を設定可能

利用方法

以下のようなファイル構成で考えます。

app/
  ├ layout.tsx
  ├ page.tsx
  ├ pageA/
  |  ├ page.tsx
  |  └ loading.tsx
  ├ @componentA/
  |  ├ page.tsx
  |  ├ default.tsx
  |  └ loading.tsx
  └ @componentB/
     ├ page.tsx
     ├ loading.tsx
     └ pageA/
        └ page.tsx

以下のように@がついたコンポーネントをlayout.tsxで引数として受け取って利用できます。
例えば、componentA/page.tsxがデータを取得して表示する、時間がかかるコンポーネントであれば、componentAが読み込み中は{props.componentA}の場所はapp/@componentA/loading.tsxで定義したものが表示されます。

app/layout.tsx
export default function Layout(props: {
  children: React.ReactNode
  componentA: React.ReactNode
  componentB: React.ReactNode
}) {
  return (
    <>
      {props.children}
      {props.componentA}
      {props.componentB}
    </>
  )
}

default.tsx

先ほどのページにも記載がありますが、配下のページ(今回だとpageA)でもcomponentAとcomponentBを正しく表示するためにはcomponentAのようにdefault.tsxを作成するか、componentBのようにpageA/page.tsxを作成して、pageAにアクセスがあった場合に、componentA・componentBに何を表示するか指定しておく必要があります。
これがないと、404エラーが発生します。

https://nextjs.org/docs/app/building-your-application/routing/parallel-routes#defaultjs

見られる順番としては以下

  1. componentBのようにページ指定の表示があるか
  2. componentAのようにdefault.tsxがあるか
  3. どちらもなければ404エラー

特にページごとに表示を切り替えたいわけでなければ、default.tsxを以下の内容にしましょう。
page.tsxの内容をそのまま返しているだけです。

default.tsx
import Page from './page'

export default async function Default() {
    return (
        <Page />
    )
}

Suspenseでのコンポーネント表示までのローディング表示について

Parallel routesではコンポーネント内にloading.tsxを配置することで、読み込みまでのローディング表示を指定できます。
ただし、Parallel routesを使わなくてもreactのSuspenseを使えば同じことが可能です。

https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming

以下のようなファイル構成で考えます。

app/
  ├ layout.tsx
  └ page.tsx
componentA/
  ├ componentA.tsx
  └ loading.tsx

Suspenseのfallbackに指定したものが、Suspenseで囲ったコンポーネントを表示するまでのローディング画面として表示されます。

app/layout.tsx
import { Suspense } from "react";
import Loading from './componentA/loadging'
import ComponentA from './componentA/componentA'

export default function Layout(props: {
  children: React.ReactNode
}) {
  return (
    <>
      {props.children}
      <Suspense fallback={<Loading />}>
        <ComponentA />
      </Suspense>
    </>
  )
}

まとめ

Parallel routesの簡単な紹介と、非同期コンポーネントで利用できるSuspenseを紹介しました。使えれば便利ですが、Parallel routesはバグが多く、Parallel routesじゃないとダメなケースはあまりないので、安定するまではなるべく避けていこうと思います。

Discussion