🍲

古いReact環境に最新Storybook(Vite)を導入する!格闘の記録と解決策

に公開

はじめに

「うちのプロジェクト、Reactのバージョンが古くてStorybookの最新版が対応してないんだよな…」
「でも、今さらプロジェクト全体のバージョンアップは工数が大きすぎて…」

こんな悩みを抱えているフロントエンド開発者は少なくないでしょう。
この記事では、まさにそんな状況下で、既存のレガシーなReact環境に大きな影響を与えることなく、最新のStorybook(Viteビルド)を導入した際の一連のプロセスを共有します。

数々のエラーとの格闘の末にたどり着いた、実践的なノウハウを詰め込みました。

採用したアーキテクチャ:サンドボックス方式 🏖️

今回の核心は、「サンドボックス(隔離環境)方式」 を採用した点です。
既存プロジェクトの中にstorybook-envという独立したNode.js環境を構築し、そこから親プロジェクトのコンポーネントを参照する構成を取りました。

ディレクトリ構成

project-root/
├── app/
│   └── javascript/
│       └── src/
│           └── dashboard/
│               └── components/ # 👈 既存のコンポーネント群
│
└── storybook-env/ # 👈 ここが独立したStorybook環境
    ├── .storybook/
    │   └── main.js # Storybookのコア設定
    ├── node_modules/
    └── package.json

この方法なら、親プロジェクトのpackage.jsonやビルド設定を一切汚すことなく、最新のツールチェインの恩恵を受けられます。しかし、この構成特有の「壁」がいくつも立ちはだかりました。

格闘の記録:エラー解決の道のり

課題1:Viteのセキュリティウォール - Failed to fetchエラー

Storybookを起動して最初に遭遇したのがこのエラーでした。

エラー内容: Failed to fetch dynamically imported module: http://localhost:6006/@fs/...

原因: Viteのセキュリティ機能 server.fs.allow が、自身のプロジェクトルート(storybook-env)外のファイルへのアクセスをデフォルトでブロックしていました。

解決策: .storybook/main.js に viteFinal 設定を追記し、Viteに対して親ディレクトリへのアクセスを明示的に許可しました。


// storybook-env/.storybook/main.js
import { mergeConfig } from 'vite';

const config = {
  stories: ['../../app/javascript/src/**/*.stories.jsx'],
  // ...
  async viteFinal(config) {
    return mergeConfig(config, {
      server: {
        fs: {
          // storiesのパスに合わせて必要な階層を許可
          allow: ['../../'],
        },
      },
    });
  },
};
export default config;

この時、ReferenceError: mergeConfig is not defined というエラーにも遭遇しましたが、これは単純にファイルの先頭でimport { mergeConfig } from 'vite';を忘れていた、という凡ミスでした。

課題2:環境のミスマッチ - The engine "node" is incompatibleエラー

エラー内容: The engine "node" is incompatible with this module.

原因: storybook-envにインストールしたviteが要求するNode.jsのバージョンが、ローカル環境のバージョンより新しかったためです。

解決策: nvm (Node Version Manager) を導入。プロジェクトで要求されるバージョンのNode.jsをインストールし、nvm useで切り替えることで解決しました。

nvmインストール直後に nvm: command not found となる場合は、ターミナルを再起動すれば直ります。

課題3:隔離された世界の掟 - コンポーネントの依存関係

これがサンドボックス方式の最も注意すべき点です。コンポーネントがreact-router-domreact-transition-groupなどのライブラリに依存している場合、そのままではStorybook上でエラーになります。

原因: storybook-envは独立した環境のため、親プロジェクトのnode_modulesを参照しません。コンポーネントやストーリーが必要とするライブラリは、storybook-env側にもインストールする必要があります。

解決策: 依存ライブラリの追加インストール: storybook-env内でyarn add react-router-dom react-transition-groupなどを実行し、必要なライブラリを追加します。

decoratorsの活用: useHistoryなどのフックに依存するコンポーネントは、.stories.jsxファイルでStorybookのdecorators機能を使ってMemoryRouterでラップします。これにより、擬似的なルーティング環境を提供できます。


// GuideButton.stories.jsx
import { MemoryRouter } from 'react-router-dom';

export default {
  // ...
  decorators: [(Story) => <MemoryRouter><Story /></MemoryRouter>],
};

まとめ:得られた知見 📚

今回の挑戦から得られた主な学びは以下の通りです。

  • サンドボックス方式は有効: レガシー環境への影響を最小限に抑えつつ、最新の開発ツールを導入する上で非常に強力な選択肢です。
  • Viteの挙動を理解する: server.fs.allowのような、ツールが持つセキュリティ思想や設定を理解することがトラブルシュートの鍵となります。
  • Storybookのdecoratorsは万能: ルーティング、テーマ、APIモックなど、コンポーネントが依存する外部環境を擬似的に作り出す上でdecoratorsは欠かせません。
  • 依存関係は二重に管理: 親プロジェクトとサンドボックス、それぞれの環境で必要な依存関係を意識することが重要です。

簡単ではありませんでしたが、一つ一つのエラーと向き合うことで、ツールの仕組みへの理解が深まりました。この記事が、同じような課題に直面している誰かの助けになれば幸いです。

SKIYAKI Tech Blog

Discussion