React 18 では、開発モードで useEffect
が 2 回実行されるようになった。これは、React の厳密モード (StrictMode
) による意図的な挙動であり、潜在的なバグを検出しやすくするための変更である。
確認した環境
この現象を確認した環境は以下の通り。
- next: v12.2.2
- react: v18.2.0
- react-dom: v18.2.0
React 18 では、開発モードで useEffect
が 2 回実行されるようになった。これは、React の厳密モード (StrictMode
) による意図的な挙動であり、潜在的なバグを検出しやすくするための変更である。
この現象を確認した環境は以下の通り。
状況に応じて、以下のいずれかの方法で対応できる。
StrictMode
コンポーネントを削除する厳密モードを無効にすることで、useEffect
が 2 回実行される問題を回避できる。ただし、厳密モードを無効にすることで、React の一部のデバッグ機能が利用できなくなる点に注意。
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
root.render(
// <StrictMode>
<App />,
// </StrictMode>
);
useEffect
内でクリーンアップ関数を定義し、複数回実行されても問題が起きないようにする。この方法は、データ取得やサブスクリプションの解除が必要な場合に有効。
const [results, setResults] = useState([]);
const [page, setPage] = useState(1);
useEffect(() => {
let ignore = false;
fetchResults(query, page).then((json) => {
if (!ignore) {
setResults(json);
}
});
return () => {
ignore = true;
};
}, [query, page]);
useRef
を利用するuseRef
を使用して、初回の実行をスキップする方法。この方法は、開発モードでのみ問題を回避したい場合に適している。
const refFirstRef = useRef(true);
useEffect(() => {
if (process.env.NODE_ENV === 'development') {
if (refFirstRef.current) {
refFirstRef.current = false;
return;
}
}
something();
}, []);
このコードでは、process.env.NODE_ENV === 'development'
の条件を追加することで、プロダクションビルド時には影響が出ないようにしている。