Chapter 3 of 4
Cleanup
Returning a teardown function, and why React runs it more often than you expect.
An effect can return a function. React calls it before running the effect again, and once more when the component unmounts. Anything the effect started must be stopped there.
useEffect(() => {
const id = setInterval(() => tick(), 1000);
return () => clearInterval(id);
}, []);
useEffect(() => {
function handleResize() { setWidth(window.innerWidth); }
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);Cleanup runs between updates, not only at the end
With dependencies, the sequence on each change is: run cleanup for the old values, then run the effect with the new ones. That is what keeps the subscription pointing at the right thing.
useEffect(() => {
const connection = connect(roomId);
return () => connection.close();
}, [roomId]);
// roomId: "general" -> "random"
// 1. close the "general" connection
// 2. open a "random" connectionStrict Mode runs effects twice
In development, React Strict Mode mounts each component, runs its effects, cleans them up, and runs them again. It is checking that your cleanup actually undoes the setup.
Cancelling stale requests
Async work needs guarding: a slow response for an old input must not overwrite a fresh one.
useEffect(() => {
let isCurrent = true;
async function load() {
const data = await fetchUser(userId);
if (isCurrent) setUser(data);
}
load();
return () => { isCurrent = false; };
}, [userId]);