npm init vite@latest msw-ts-app
cd msw-ts-app
npm install
npm i -D msw
mkdir src/mocks
vi src/mocks/handlers.ts
handlers.ts を以下の内容で保存する
import { rest } from 'msw';
import mockUser from 'mocks/resolvers/mockUser';
const handlers = [
rest.get('/users/', mockUsers),
rest.get('/users/:id', mockUser),
];
export default handlers;
mkdir src/models
vi src/models/User.ts
以下の内容で保存
export type User = {
id: number;
username: string;
age: number;
};
mkdir src/mocks/resolvers
vi src/mocks/resolvers/mockUser.ts
mockUser.ts を以下の内容で保存する
import { ResponseResolver, MockedRequest, restContext } from 'msw';
import { User } from '../../models/User';
const users: User[] = [
{
id: 1,
username: '山田 太郎',
age: 25,
},
{
id: 2,
username: '斎藤 次郎',
age: 37,
},
{
id: 3,
username: '山田 花子',
age: 41,
},
];
const mockUser: ResponseResolver<MockedRequest, typeof restContext> = (
req,
res,
ctx
) => {
// パスパラメーターの取得
const { id } = req.params;
const user: User | undefined = getMockUserData(Number(id));
return res(ctx.json(user));
};
const getMockUserData = (id: number): User | undefined => {
// idでusersを検索
const user = users.find((user) => user.id === id);
return user;
};
export default mockUser;
続けて mockUsers.ts を以下の内容で保存する
vi src/mocks/resolvers/mockUser.ts
import { ResponseResolver, MockedRequest, restContext } from 'msw';
import { User } from '../../models/User';
const users: User[] = [
{
id: 1,
username: '山田 太郎',
age: 25,
},
{
id: 2,
username: '斎藤 次郎',
age: 37,
},
{
id: 3,
username: '山田 花子',
age: 41,
},
];
const mockUser: ResponseResolver<MockedRequest, typeof restContext> = (
req,
res,
ctx
) => {
// クエリパラメーターの取得
const perPage = req.url.searchParams.get('perPage');
const user: User[] = getMockUserList(Number(perPage));
return res(ctx.json(user));
};
const getMockUserList = (perPage: number): User[] => {
return users.slice(0, perPage);
};
export default mockUser;
vi src/mocks/browser.ts
browser.ts を以下の内容で保存する
import { setupWorker } from 'msw';
import handlers from 'mocks/handlers';
const worker = setupWorker(...handlers);
export default worker;
src/main.tsx に以下を追加
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
// msw(追加)
import worker from './mocks/browser';
// msw(追加)
if (process.env.NODE_ENV === 'development') {
void worker.start();
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
次の準備されているコマンドを実行するだけでOKです
npx msw init public/ --save
これで準備が整いました。
npm run dev
http://localhost:5174/ へアクセスして、ブラウザの console を確認する
[MSW] Mocking enabled.
と表示されていれば起動は成功しています。
vi src/useUser.ts
import { useEffect, useState } from 'react';
import { User } from './models/User';
export function useUser(id: number) {
const [data, setData] = useState<User | undefined>(undefined);
useEffect(() => {
const fetchDataAsync = async () => {
try {
const url = `/users/${id}`;
const res = await fetch(url);
const data = await res.json();
setData(data);
} catch (e) {
throw new Error('fetch error');
}
};
fetchDataAsync();
}, [id]);
return { data };
}
vi src/useUsers.ts
import { useEffect, useState } from 'react';
import { User } from './models/User';
export function useUsers(perPage: number) {
const [users, setUsers] = useState<User | undefined>(undefined);
useEffect(() => {
const fetchDataAsync = async () => {
try {
const url = `/users/?perPage=${perPage}`;
const res = await fetch(url);
const users = await res.json();
setUsers(users);
} catch (e) {
throw new Error('fetch error');
}
};
fetchDataAsync();
}, [perPage]);
return { users };
}
App.tsx を以下のように変更する
import { useRef, useState } from 'react';
import './App.css';
import { useUser } from './useUser';
import { useUsers } from './useUsers';
function App() {
const [id, setId] = useState<number>(1);
const { data } = useUser(id);
const { users } = useUsers(2);
const ref = useRef<HTMLInputElement>(null);
const handleGetUser = () => {
if (ref.current?.value) setId(Number(ref.current.value));
};
return (
<>
<h1>app</h1>
<hr />
<pre style={{ textAlign: 'left' }}>{JSON.stringify(users, null, 2)}</pre>
<hr />
<div>
id : <input type="text" ref={ref} onChange={handleGetUser} />
</div>
<pre style={{ textAlign: 'left' }}>{JSON.stringify(data, null, 2)}</pre>
</>
);
}
export default App;