2024-05-21
MSWで地味にハマったメモ
MSWのv2の導入でハマったことのメモ。
環境
"react": "^18.2.0",
"vite": "^4.3.2"
"msw": "^2.3.0",
バックエンドのAPIがまだ開発されていない段階で、フロントエンドのUIを作成することになったため、効率的に開発を進めるためにMSWを導入した。
以下のような形で handler.ts
と browser.ts
を用意し、main.tsxにセットアップ
handler.ts
import { http, HttpResponse, type ResponseResolver } from 'msw'
const mockTest: ResponseResolver = () => {
return HttpResponse.json(
{
text: "test"
}
)
}
export const handlers = [
http.get(`http://localhost:8000/test`, mockTest),
http.get(`http://localhost:8000/ping`, mockTest),
browser.ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export async function setupMsw() {
if (import.meta.env.DEV && import.meta.env.VITE_USE_MSW === 'true') {
const worker = setupWorker(...handlers)
worker.start()
}
}
main.tsx
import { createRoot } from "react-dom/client";
import React from "react";
import { Routes } from "@generouted/react-router";
import { setupMsw } from "./mocks/browser";
// VITE_USE_MSWがtrueまたはdevで実行時のみ、MSWが起動
setupMsw()
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const container = document.getElementById("root")!;
createRoot(container).render(
<React.StrictMode>
<Routes />
</React.StrictMode>
);
何がおこったか
import React, { useEffect } from "react";
function TestIndexPage() {
useEffect(() => {
(async () => {
const fet = await fetch("http://localhost:8000/test")
const res = await fet.json()
console.log("fet", res)
const ping = await fetch("http://localhost:8000/ping")
const pingres = await ping.json()
console.log("pingres", pingres)
})()
}, [])
return (
<div>
<p>test</p>
</div>
);
}
export default TestIndexPage;
Testページで上記のようにfetchすると、/testは404になるが、/pingはMSWでセットしたレスポンスが返るようになっていた。
結論
MSWのworker.setup() は非同期処理のため、setupが終了する前にレンダリングが実行され、初回のリクエスト時 (/testのfetch時) はまだworkerが動いていなかったため。
こんな感じで、遅れて「Mocking Enabled」が表示される
![画像が読み込まれない場合はページを更新してみてください。](https://prod-files-secure.s3.us-west-2.amazonaws.com/993e6a0e-70bf-4d45-ab38-efb7474f6d2b/9f2df56f-2810-4928-a046-fba19010af05/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240726%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240726T100952Z&X-Amz-Expires=3600&X-Amz-Signature=79d25a0070325cdee7b412f8820dfb8e6410dc94f2fc82cf9f559d0d504f3295&X-Amz-SignedHeaders=host&x-id=GetObject)
以下のように、browser.tsとmain.tsxでの記述を、worker.start() の非同期処理終了後にレンダリングするようにして解決。
browser.ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export async function setupMsw() {
if (import.meta.env.DEV && import.meta.env.VITE_USE_MSW === 'true') {
const worker = setupWorker(...handlers)
await worker.start()
}
}
main.tsx
import { createRoot } from "react-dom/client";
import React from "react";
import { Routes } from "@generouted/react-router";
import { setupMsw } from "./mocks/browser";
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const container = document.getElementById("root")!;
// VITE_USE_MSWがtrueまたはdevで実行時のみ、MSWが起動
setupMsw().then(() => {
createRoot(container).render(
<React.StrictMode>
<Routes />
</React.StrictMode>
);
})
無駄にハマったので自戒としてメモ…
参考