概要
React + Vite + json-server を使った開発環境で、子コンポーネントでPATCH処理を実行すると親コンポーネントが予期せず再レンダリングされる現象に遭遇しました。
子コンポーネントは親の状態を一切更新していないし、何をトリガーに再レンダリングされるのか分からずハマってしまいました。
結果的には ViteのHMR(Hot Module Replacement) が原因でした。
現象
子コンポーネントでjson-serverにPATCH リクエストを送信すると、親コンポーネントが即時再レンダリングされる。
TaskList.tsx(親コンポーネント)
const fetchTasks = async (): Promise<Task[]> => {
console.log("APIからタスクを取得中...");
const response = await fetch("http://localhost:3000/tasks");
return response.json();
};
const fetchTasksPromise = fetchTasks();
export function TaskList() {
const tasks = use(fetchTasksPromise);
return (
// 〜省略〜
{tasks.map((task) => {
return <TaskItem key={task.id} task={task} />;
})}
// 〜省略〜
);
};
TaskItem.tsx(子コンポーネント)
export function TaskItem({ task }: { task: Task }) {
const handleComplete = async () => {
await fetch(`http://localhost:3000/tasks/${task.id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ completed: !task.completed }),
});
};
return (
// 〜省略〜
<button onClick={handleComplete}>
ボタン
</button>
// 〜省略〜
);
};
原因
以下の流れで親コンポーネントが再レンダリングされていたようです。
- PATCH処理で
db.json
が更新される - Viteが
db.json
の変更を検知する - HMRが発動してモジュールが再読み込みされる
- 再読み込みにより
fetchTasksPromise
が再作成される - データが更新されているため
TaskList
が再レンダリングされる
解決方法
ズバリdb.json
を監視対象から除外すればOKです。
vite.config.js
export default {
// 〜既存の設定〜
server: {
watch: {
ignored: [
'**/db.json', // json-serverのDBファイルを除外
]
}
}
}
まとめ
HMRって自分でファイルを編集したとき以外にも発動するんだ〜って勉強になりました。(あたりまえか…)