🥂

StorybookでuseRouter()を使ったらエラーが出た!

2024/04/16に公開

はじめに

Storybookを使用していたらエラーが発生したので、どうしてそのエラーが出たのか調べてみて、対応を書きました🙌

Storybookとは

こちらに'Storybookは「UIカタログ」です。それぞれのUIコンポーネントをブラウザで手軽にチェックすることができます。'と書いてありました!

とてもわかりやすく書いてあるので、ご覧ください!
https://zenn.dev/fullyou/articles/853b77a3ce9144
公式も貼っておきます。
https://storybook.js.org/

コード解説

Storybookで、Headerコンポーネントを表示しようとしました。

import { Header } from '.';

export const Story = () => (
  <>
    <Header />
  </>
);

export default {
  title: 'components/Header',
  component: Header,
  tags: ['autodocs'],
};

export defaultとは?

Storybookにコンポーネントを認識させる記述。
https://storybook.js.org/tutorials/intro-to-storybook/react/ja/simple-component/

tags: ['autodocs']とは?

コンポーネントのドキュメントを自動生成するコード。

https://zenn.dev/sa2knight/books/storybook-7-with-vue-3/viewer/addon_docs#ドキュメントを自動生成する

エラー内容

Storybookで、Headerコンポーネントを表示しようとした際に、下記のエラーが発生しました。

Error

Error: invariant expected app router to be mounted
訳: 不変条件はがapp routerがマウントされることを期待している

App Routerが適切にマウントされていないということだと思います!

App Routerとは?

src/app配下にフォルダやファイルを置く事で、ルーティングできます。
https://zenn.dev/collabostyle/articles/7377d383430bf3

マウントとは?

DOMツリーに要素を追加していくこと。簡単にいえば、該当のReactコンポーネントを画面に表示するために最初に行われる処理のこと。
ツリーから要素を削除していくことをアンマウントという。
https://de-milestones.com/react-mount-rendering/
https://qiita.com/sazumy/items/abfecdf37fc3cef5c8e4

対応

<Header />内でuseRouter()を使用して、ページ遷移をする処理を書いていたのでそれが原因だと思います。

下記のように書いたら直りました。

import { Header } from '.';

export const Story = () => (
  <>
    <Header />
  </>
);

export default {
  title: 'components/Mypage/Header',
  component: Header,
  //ここを追加
  parameters: {
    nextjs: {
      appDirectory: true,
    },
  },
  tags: ['autodocs'],
};

こちらのコメントを参考にして書きました!
https://github.com/storybookjs/storybook/issues/24722#issuecomment-1806848367

useRouter()とは?

ルーティングに関連する機能をコンポーネント内で利用するためのもの
https://nextjs.org/docs/pages/api-reference/functions/use-router

parametersとは?

ストーリーの振る舞いをカスタマイズするために追加される設定
https://storybook.js.org/docs/writing-stories/parameters

appDirectory: trueとは?

公式にはこのように書いてありました。

If your story imports components that use next/navigation, you need to set the parameter nextjs.appDirectory to true in for that component's stories:

訳: ストーリーがnext/navigationを使用するコンポーネントをインポートする場合、そのコンポーネントのストーリーでパラメータnextjs.appDirectoryをtrueに設定する必要があります。

If your Next.js project uses the app directory for every page (in other words, it does not have a pages directory), you can set the parameter nextjs.appDirectory to true in the .storybook/preview.js|ts file to apply it to all stories.

訳: Next.jsプロジェクトがすべてのページにappディレクトリを使用する場合(言い換えれば、pagesディレクトリを持たない場合)、.storybook/preview.js|tsファイルでnextjs.appDirectoryパラメータをtrueに設定すると、すべてのストーリーに適用されます。

つまり、appDirectory: trueは、app routerを適応させるためのものだと思います!

https://storybook.js.org/docs/get-started/nextjs

appDirectory: trueは、App Routerを適応させるためのものということは、Page Routerの時のuseRouter()を使用する時にはつけなくても大丈夫なのか?とか気になりましたが、そこら辺は今回は省略し、別記事に余裕があれば書こうと思います!

まとめ

今回は、Storybookでinvariant expected app router to be mountedのエラーが発生した原因について調べてみました!

コンポーネント内で、useRouter()などのページ遷移の処理を行う時は、parameterにnextjsのappDirectory: true,を記述したらStorybookがApp Routerに適応されるということが分かりました!

同じエラーに直面した方の参考になると嬉しいです!

Discussion