React · Frontend system thinking
Why does useEffect return a cleanup function?
Short answer
Cleanup undoes an effect's setup before it runs again or when the component unmounts. It prevents duplicate subscriptions, leaked timers and updates from stale work.
Effects describe a synchronization process. Setup starts it and cleanup stops it. React calls cleanup before re-running the effect with new dependencies and once when the component unmounts. Without cleanup, every re-run can add another listener, timer or connection.
function Timer({ intervalMs }) {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setSeconds((current) => current + 1);
}, intervalMs);
return () => clearInterval(id);
}, [intervalMs]);
return <p>{seconds} seconds</p>;
}What cleanup is for
- Removing event listeners and unsubscribing from stores
- Clearing timers and animation frames
- Aborting requests or ignoring their late results
- Disconnecting sockets and releasing third-party instances
useEffect(() => {
let ignore = false;
async function load() {
const result = await fetchProfile(userId);
if (!ignore) {
setProfile(result);
}
}
load();
return () => {
ignore = true;
};
}, [userId]);Cleanup is about correctness, not only memory. A late response or a duplicated listener can make the UI show the wrong state even if nothing leaks. The effect should be written so that setup, cleanup and another setup leaves the same result as a single setup.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1When exactly does React run effect cleanup?
- 2How do you cancel a fetch when dependencies change?
- 3Why does StrictMode mount, unmount and remount effects in development?
- 4What happens if cleanup removes a listener that was never added?
Say it in the interview
Cleanup is the teardown half of an effect. React runs it before the effect runs again and when the component unmounts. I use it to remove listeners, clear timers, disconnect sockets and invalidate in-flight requests. The goal is for setup and cleanup to be symmetrical, so repeated runs do not duplicate subscriptions or apply stale results.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live React mock interview and get scored feedback on depth, clarity and confidence.