50
32

useEffectがマウント時に2回実行される

Last updated at Posted at 2022-08-28

環境

  • OS: Windows 10
  • Node.js: v16.16.0
  • React: 18.2.0

現象

useEffectでマウント時に1回fetchでAPIにアクセスして情報をとってくると・・・

React
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を無効にする

  • 手っ取り早いのはこれ。
React
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を見つけてドキュメントにも反映されていたので
すぐに原因が分かって良かったです。
とはいえ、ちょっと不便だなあ・・

参考文献

50
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
32