Open9

Next.js - Parallel Routesで条件に応じてServer Componentsを実行しないようにしたい

Tatsushi KiryuTatsushi Kiryu

最近、とある詳細情報と、さらにその詳細情報に関連した情報をタブ分けしたUIの詳細画面を、Parallel Routesを使って実装しようと試みている。

---------------------------------------------
何かの詳細情報
---------------------------------------------
| Tab A | Tab B | Tab C |
| コンテンツはタブ選択した際に取得したい |
---------------------------------------------
Tatsushi KiryuTatsushi Kiryu

ディレクトリ構成は以下

- app 
  - list
    - [id]
      - page.tsx
      - layout.tsx
      - @a
        - page.tsx
      - @b
        - page.tsx
      - @c
        - page.tsx
Tatsushi KiryuTatsushi Kiryu

layout.tsx を以下のように実装することで、タブのコンテンツを出し分けることはできた。

ちなみに、タブの選択状態を query string に持たせるようにしたので、layout.tsx でuseSearchParams を使わないといけなかった。そのため'use client'を付与している。

layout.tsx
'use client';

export default function Layout({
  children,
  a,
  b,
}: {
  children: React.ReactNode;
  a?: React.ReactNode;
  b?: React.ReactNode;
  c?: React.ReactNode;
}) {
  const searchParams = useSearchParams();
  const tab = searchParams.get('tab') ?? 'a';
  return (
    <>
      {children}
      <Tabs
        selectedTab={tab}
      />
      <TabPanel>
        {match(tab)
          .with('a', () => a)
          .with('b', () => b)
          .with('c', () => c)
          .otherwise(() => false)}
      </TabPanel>
    </>
  );
}
Tatsushi KiryuTatsushi Kiryu

表示の切り替えは実現できたものの、tab a, b, c, の page.tsx 自体は実行されてしまっており、表示しないタブの情報を無駄に取得してしまっていることがわかった。

Tatsushi KiryuTatsushi Kiryu

その後は試していないが、おそらく以下のように、自分のタブが選択されているかどうかをチェックすれば、防げるはず。

export default async function PageA({ searchParams }) {
  const tab = searchParams?.tab ?? 'a';
  if (tab !== 'a') return;
  // awesome api call
  return <PageAComponent />
}
Tatsushi KiryuTatsushi Kiryu

ただし、どのタブが選択されているかを複数箇所(layout.tsxと、各tab a, b, cのpage.tsx)でチェックしなければいけないので、イマイチ。

Tatsushi KiryuTatsushi Kiryu

公式ドキュメントでは、@loginと@dashboardのParallel Routesを例にしたユースケースが挙げられていたけれど、@dashboardのpage.tsxは、ログインしているしていないに関わらず実行されていそう。

果たしてどうか。

Tatsushi KiryuTatsushi Kiryu

今回の場合、結局、詳細画面のpage.tsxでtabの出し分けを制御する方法に落ち着いたし、こちらの方がシンプルだった。

// 詳細画面のpage.tsx
export default async function Page({ searchParams }) {
  const tab = searchParams.'tab' ?? 'a';
  return (
    <>
      <PageDeital />
      <Tabs
        selectedTab={tab}
      />
      <TabPanel>
        <Suspense fallback={<Loading />}>
          {match(tab)
            .with('a', () => <TabA />)
            .with('b', () => <TabB />)
            .with('c', () => <TabC />)
            .otherwise(() => false)}
        </Suspense>
      </TabPanel>
    </>
  );
}

// TabAのServer Component
export async function TabA() {
  // awesome api call
 return <TabAComponent />
}
Tatsushi KiryuTatsushi Kiryu

ということで、Parallel Routesは条件分岐で出し分けするようなケースには適していなさそう、という一旦の結論。

自分の使い方が間違っている可能性もあるので、もう少し追ってみる。