環境
- OS: Windows 10
- Node.js: v16.16.0
- React: 18.2.0
現象
useEffectでマウント時に1回fetchでAPIにアクセスして情報をとってくると・・・
useEffect(() => {
fetch("https://xxxx.com/xxx")
.then(res => res.json())
.then(data => {
console.log(data)
})
} , []);
2回fetchされている!!
Why Japanese People!?!?(古い)
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
原因
React18の、StrictModeの追加機能によるものだった。
when running in “strict mode“ React will intentionally double-render components for in order to flush out unsafe side effects.
「ストリクトモード」で実行すると、安全でない副作用を洗い出すために、React は意図的にコンポーネントを二重レンダリングします。
引用元:
StrictModeが有効な場合は、コンポーネントが2回レンダリングされる。
そのため、useEffectが2回実行されて、fetchの結果が2つ返ってきた、ということになる。
以下のGitHubのissueで議論されていた
回避策
他にもありそうだが、見つけたものだけ挙げてみた。
方法1: StrictModeを無効にする
- 手っ取り早いのはこれ。
root.render(
- <React.StrictMode>
<App />
- </React.StrictMode>
);
方法2: 2回目の実行結果を無視する
公式にこんな方法があった。
If your Effect fetches something, the cleanup function should either abort the fetch or ignore its result:
Effect が何かを取得した場合、クリーンアップ関数は取得を中断するか、その結果を無視する必要があります。
useEffect(() => { let ignore = false; async function startFetching() { const json = await fetchTodos(userId); if (!ignore) { setTodos(json); } } startFetching(); return () => { ignore = true; }; }, [userId]);
引用元:
感想
最初は戸惑いましたが、issueを見つけてドキュメントにも反映されていたので
すぐに原因が分かって良かったです。
とはいえ、ちょっと不便だなあ・・
参考文献