Effects synchronize with external systems
Think of useEffect as keeping something outside React in sync with your props and state, not as lifecycle methods. Learn when effects run, why cleanup runs before every re-run, and why StrictMode mounts twice.
Comes up in: React, Recruiter screen
Learn#
Sana’s chat page stays connected to the “general” room after the user switches to “travel”. Her effect has an empty dependency array, because she thought of it as “on mount”.
An effect keeps an external system, such as a connection, a subscription or a browser API, in step with your props and state. That is synchronization: whenever the values it uses change, stop syncing the old values and start syncing the new ones:
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register(); // a DOM for Node; in a browser you already have one
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
const { createElement: h, useEffect, act } = await import("react");
const { createRoot } = await import("react-dom/client");
function ChatRoom({ room, theme }) {
useEffect(() => {
console.log(`connect to ${room}`);
return () => console.log(`disconnect from ${room}`);
}, [room]); // re-sync only when room changes
return h("h2", { className: theme }, room);
}
const root = createRoot(document.createElement("div"));
await act(() => root.render(h(ChatRoom, { room: "general", theme: "light" })));
await act(() => root.render(h(ChatRoom, { room: "general", theme: "dark" }))); // theme only
await act(() => root.render(h(ChatRoom, { room: "travel", theme: "dark" })));
await act(() => root.unmount());Output (verified)
connect to general disconnect from general connect to travel disconnect from travel
The cleanup function runs before the effect runs again, and once more when the component unmounts.
Console
- connect to general
State
empty
Mount with room "general". After paint, the effect connects to general.
Read the steps as text
- Mount with room "general". After paint, the effect connects to general.
- theme changes to dark. room did not change, so the effect does not run again.
- room changes to travel. First the old cleanup runs with the old room, then the effect runs with the new one.
- Unmount: the last cleanup runs. Every connect has exactly one matching disconnect.
Predict#
Predict first
Is useEffect(fn, []) the same as componentDidMount?
Both run once after mounting, but the effect runs after the browser paints, sees the first render’s values (a closure), and must pair with its cleanup, which StrictMode tests by mounting twice. Thinking in lifecycles leads to missing dependencies and stale values.
Interview angle#
Say it like this — about 30 seconds
“An effect synchronizes something outside React with the current props and state. It runs after the browser paints. The dependency array is not a lifecycle choice; it lists every reactive value the effect uses, so React re-runs it when one of them changes. Before each re-run, and on unmount, React runs the previous cleanup. In development, StrictMode mounts, unmounts and mounts again to prove the cleanup works. Many effects should not exist at all: derive data during render, and handle events in handlers.”
Follow-up When does the effect cleanup function run?
Follow-up Give examples of effects you should remove.
Follow-up Why is useEffect with an empty array not the same as componentDidMount?
Practice#
Guided
Simulate React’s effect timing for a list of renders, with and without StrictMode.
Done when
- The effect re-runs only when a dependency changed, after the previous cleanup.
- Unmount runs the last cleanup.
- Strict mode adds one cleanup and re-run right after the first run.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
When does useEffect run?
After the render is committed and the browser has painted.
When does an effect's cleanup run?
Before the effect re-runs, and on unmount.
What should the dependency array contain?
Every reactive value the effect uses.
What does StrictMode do to effects in development?
Runs setup, cleanup, then setup again on mount.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
Check yourself: What are reactive values?
Props, state and anything computed from them during render.
How it works underneathSenior rounds
Each render has its own effect and cleanup, closing over that render’s values. So the cleanup for “general” disconnects from general, even after room is “travel” Source: React: Lifecycle of reactive effects.
Check yourself: Which values does a cleanup see?
The values from the render whose effect it cleans up.
Edge cases and trapsWhere people slip
Check yourself: Does StrictMode's double mount happen in production?
No, only in development.
In productionStaff-level answers
Check yourself: Name an effect timeline step that is easy to forget.
Cleanup on unmount, for timers, listeners and connections.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- When does the effect cleanup function run?
- Give examples of effects you should remove.
- Why is
useEffectwith an empty array not the same ascomponentDidMount?