React useEffect runs twice on mount
Table of Contents
useEffect Running Twice on Mount During Development Is by Design
This is a personal memo from a React beginner.
This is a specification from React 18.
useEffectbehaves this way for all components only during development when StrictMode is enabled.
When
useEffectfetches information from an API once on mount, it's fetched twice. This was due to an added feature of StrictMode in React 18.
when running in “strict mode“ React will intentionally double-render components for in order to flush out unsafe side effects.
What This Means
Imagine the
ChatRoomcomponent is part of a large application with many different screens. The user initially opens the ChatRoom page. The component mounts, and theconnect()side effect is called. Then, let's say they navigate to another screen, like the settings page. TheChatRoomcomponent unmounts. After that, if the user clicks "Back," theChatRoommounts again. This results in a second connection being established. However, the initial connection was not torn down. As the user navigates through pages in the application, connections accumulate.Such bugs are easily missed without extensive manual testing. To help quickly find these issues, React re-mounts every component immediately after its initial mount during development. By seeing "Connecting..." logged twice, you are more likely to notice the real problem: your code is not closing the connection when the component unmounts. To fix this problem, return a cleanup function from
useEffect.
In other words, code that behaves strangely due to multiple mounts is bad code (a bug), so React intentionally runs it twice during development to help you identify it.
Countermeasures and Philosophy
useEffect runs twice on mount during development, but only once in a production environment. Since this is the correct behavior according to React, it's necessary to implement code that aligns with this philosophy. It seems to be saying that "multiple components can run in parallel asynchronously, and re-mounting after initial mount can occur," so handle it appropriately.
- "Disabling StrictMode" is probably not a good idea.
- Implement a cleanup function to perform cleanup.
Typically, you handle this by implementing a cleanup function. What a cleanup does is stop or undo everything the effect was doing. Most users won't notice the difference between the single effect (in production) and the effect → cleanup → effect sequence (in development).
React always calls the cleanup function before the effect is re-executed. It also performs cleanup when the component finally unmounts (is removed).
useEffect(() => {
connect();
return () => { // ★★ Cleanup process
disconnect();();
};
}, []);
useEffect(() => {
let ignore = false;
async function startFetching() {
const json = await fetchTodos(userId);
if (!ignore) {
setTodos(json);
}
}
startFetching();
return () => { // ★★ Cleanup process
ignore = true;
};
}, [userId]);
How to perform cleanup needs to be considered for each specific process.
References
React + TypeScript: React 18でコンポーネントのマウント時にuseEffectが2度実行されてしまう useEffectがマウント時に2回実行される React(公式)Synchronizing with Effects
