※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【React18】StrictModeは開発時にはuseEffectが2回呼び出される

react web開発 フロント開発 react18 StritMode

投稿日:2022年11月7日

このエントリーをはてなブックマークに追加
React18で導入されたStrictModeを使用すると、開発時にコンポーネントのマウントは2度行われます。この記事では、その問題について詳しく解説します。

はじめに

この記事について

今回、Reactを使用した開発で開発環境でのみuseEffect()が想定外の挙動をしてしまったために、メモを残すことにしました。

この予想外の挙動というのは、React側で意図された挙動であるため、ドキュメントをちゃんと読んでいれば想定通りなものである点に注意してください。

環境

この記事のコードは以下の環境で実行しています。

  • node version v16.17.0
  • create-react-app version 5.0.1
  • react version 18.2.0
  • serve version 14.0.1

実践

発生した問題

今回の問題は以下のようなコンポーネントで発生しました。

App.tsx
import React, { useEffect } from 'react';

const App = () => {

  // マウント時に1度だけ呼び出される
  useEffect(()=>{
    console.log(`useEffect called!`)
  },[])
  
  return (
    <div>
      サンプルページ
    </div>
  );
}

export default App;

このコンポーネントはuseEffect()に渡したログ出力の関数が、マウント時に1度だけ実行されるはずです。


しかし、実際にnpm run startで開発用サーバーを起動してログを確認すると…

▲ブラウザのログ出力

このように、本来1度しか呼ばれないはずのuseEffectのコールバックが2回呼び出されてしまっています。

この挙動は開発時のみで、以下のようにビルド後のアプリケーションでは発生しません。

ターミナル
$ npm run build  
$ npx serve -s build -l 3000
▲ブラウザのログ出力

問題の原因

問題の原因はcreate-react-appのテンプレートのindex.tsxにあります。

index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

<App />コンポーネントは<React.StrictMode />コンポーネントの子要素として使用されています。StrictModeというのは、エラーなるほどの重大な問題ではないが、潜在的な問題を見つけ出すことができるコンポーネントです。StrictModeのドキュメントを読んでいくと、その中に「Ensuring reusable state」という項目があります。
この項目によると、

将来的にブラウザバックなどの機能を追加していく際に、コンポーネントは破壊と再構築に対して柔軟に対応できる必要がある。この柔軟性をチェックするためにStrictModeは、開発時にコンポーネントが初めてマウントされる時に、一度アンマウントして再度マウントし直す

つまり、<StrictMode />の内部のコンポーネントは開発時には常にマウントが2度行われてしまうんですね。

解決方法

基本的にはマウントがやりなおされても問題なく作成するべきではありますが、この挙動によってアプリケーションの正常な挙動が確認できない場合には、下のように<StriceMode />をコメントアウトしましょう。

index.tsx
// [...]

root.render(
  // <React.StrictMode>
    <App />
  // </React.StrictMode>
);

// [...]
このエントリーをはてなブックマークに追加


関連記事

記事へのコメント
1:おがわ
2023年1月19日23:09

しっかりと原因を調査する姿勢、素晴らしいですね!

2:MarsQuai
2023年2月19日2:35

ありがとうございます!