👌

Vite + StoryBookにMSWを導入してみた

2023/01/01に公開

概要

起動が高速で、TSのライブラリのが設定なしで利用できたり、既にメリットについては、様々な記事で取り上げられています。
StoryBookと記載していますが、StoryBookはあれこれは実装していません。MSW導入で必要になった部分だけ実装を追加しています。なぜStoryBookを使用するのか、それは以下の記事あたりをご確認ください。

そしてMSW(Mock Service Worker)について、既に使用されたことがある方は、おなじみの書き方なので、大変ではないと思います。
またMSWは、StoryBookとのコラボに限らず、実際のデータを取らずに、ダミーのデータを返すで開発を進められるので、非常に役立つものだと思ってます。

今回は以下手順で進めていきます。

  1. viteでReactプロジェクトを作成
  2. StoryBookを導入
  3. MSW(Mock Service Worker)を導入
  4. MSW機能確認用の画面を作成
  5. ルーティング設定をする
  6. MSWの機能確認
完成後のコードおいときます

https://github.com/eno-conan/storybook-vite-msw

実際にすすめていきます

viteでReactプロジェクトを作成

  1. ターミナルを開き、以下コマンドを実行
 npm create vite@latest
  1. Project name,framework,variantを聞かれるので、それぞれ選択します(Project nameは任意の名前で構いません)
  2. 作成したプロジェクトのフォルダにcd XXXで移動のうえ、npm inpm run devの順に実行し、起動します。
  3. http://127.0.0.1:5173、にアクセスして以下のような画面が表示されれば、手順1は完了です

StoryBookを導入

  1. @storybook/builder-viteをインストールするため、以下コマンドを実行(1-2分くらいかかると思います)
 npx storybook init --builder @storybook/builder-vite
  1. 最後、npm run storybookを含んだ以下のようなものがターミナルに表示されます
  2. npm run storybookを実行して、以下の画面が表示されれば、手順2は完了です

MSW(Mock Service Worker)の導入

  1. MSWとアドオンをインストール
 npm i msw msw-storybook-addon -D
  1. MSWのServiceWorkerを生成
 npx msw init public/ --save
  1. publicフォルダ直下に、mockServiceWorker.jsというファイルが作成されます。(画像はVsCode)
  2. AddOnの設定
    .storybook/preview.cjs
    import { initialize, mswDecorator } from 'msw-storybook-addon';
    // Initialize MSW
    initialize();
    // Provide the MSW addon decorator globally
    export const decorators = [mswDecorator];
    
  3. static directory指定
    .storybook/main.cjs
    module.exports = {
    // ...
    staticDirs: ['../public'],
    };
    
  4. MSWの起動確認のため、一度サーバを停止して、再度以下コマンドを実行
 ```
 npm run storybook
 ```
  1. 立ち上がったら、F12でDevToolを開き、以下のように表示されることを確認されれば、手順3は完了です。

MSW表示用の画面を作成

  1. src/storiesフォルダに2ファイル作成します。(MswPage.tsx,MswPage.stories.tsx)
  2. 各ファイルの内容
    1. MswPage.tsx
    ./src/storybook/MswPage.tsx
    import React, { useEffect, useState } from 'react'
    
    const MswPage = () => {
        const { status, data } = useFetchData();
    
        if (status === 'loading') {
            return <p>Loading...</p>;
        }
        if (status === 'error') {
            return <p>There was an error fetching the data!</p>;
        }
        return (
            <>
                {data && data.map((d, idx) => (
                    idx % 20 == 0 ? d.title : ''
                ))}
            </>
        );
    }
    
    export default MswPage
    
    interface IResData {
        id: number;
        userId: number;
        title: string;
        completed: boolean;
    }
    
    function useFetchData() {
        const [status, setStatus] = useState('idle');
        const [data, setData] = useState<IResData[]>();
        useEffect(() => {
            setStatus('loading');
            fetch('https://jsonplaceholder.typicode.com/todos/')
                .then((res) => {
                    if (!res.ok) {
                        throw new Error(res.statusText);
                    }
                    return res;
                })
                .then((res) => res.json())
                .then((data) => {
                    setStatus('success');
                    setData(data);
                })
                .catch(() => {
                    setStatus('error');
                });
        }, []);
        return {
            status,
            data,
        };
    }
    
    1. MswPage.stories.tsx
    ./src/storybook/MswPage.stories.tsx
    import React from 'react';
    
    import { rest } from 'msw';
    import MswPage from './MswPage';
    import { ComponentStory } from '@storybook/react';
    
    export default {
        title: 'MswPage',
        component: MswPage,
    };
    
    //👇The mocked data that will be used in the story
    const TestData = [
        {
            id: 1,
            userId: 1,
            title: 'Something',
            completed: false
        },
    ];
    
    const PageTemplate: ComponentStory<typeof MswPage> = () => <MswPage />;
    
    export const MockedSuccess = PageTemplate.bind({});
    MockedSuccess.parameters = {
        msw: [
            rest.get('https://jsonplaceholder.typicode.com/todos/', (_req, res, ctx) => {
                return res(ctx.json(TestData));
            }),
        ],
    };
    

ルーティング設定をする

  1. react-router-domの最新版をインストール
npm install react-router-dom
  1. プロジェクトのルートディレクトリに、router.tsxを作成(ファイル名は任意)
./router.tsx
 import { BrowserRouter, Routes, Route } from "react-router-dom";
 import MswPage from "./stories/MswPage";
 import NotFound from "./stories/NotFound";
 import { Page } from "./stories/Page";

 export const Router = () => {
     return (
         <BrowserRouter>
             <Routes>
                 <Route index element={<MswPage />} />
             </Routes>
         </BrowserRouter>
     );
 };
  1. main.tsxにルーティングを適用できたら、手順5は完了です
./main.tsx
 import React from 'react'
 import ReactDOM from 'react-dom/client'
 import './index.css'
 import { Router } from './router'

 ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
 <React.StrictMode>
     <Router />
 </React.StrictMode>,
 )

MSWの動作確認

  1. StoryBookの画面を確認します。
  2. 左上のメニューからMswPageをクリックし、以下のように表示されていればOkです。
  3. またF12からDevToolの表示を確認し、以下のように表示されていれば、Mockが機能していると判断できます。これで手順6は終了し、すべて完了です。

まとめ

特に手順4について、細かいコードの説明ができていないので、そこは追記したいと思いますが、
大まかな流れについては記載できたと思います。
MSWとStoryBookの連携に関して、案外簡単にできることをしることができました。

Discussion