The cleanup function is recreated every time the useEffect
updater function is called. The current cleanup function would be called when the dependency changes (or on each render if no dependency), or before the component is unmounted. After the cleanup is called, the updater would be called, and a new cleanup function can be returned.
You can return a different function, or none at all, whenever the updater function is called.
For example, click the Inc button multiple times, and you can see in the console that the cleanup function exists only for even counter
numbers, because it's returned conditionally.
const { useState, useEffect } = React;
const Demo = () => {
const [counter, setCount] = useState(0);
useEffect(() => {
console.log(`Effect ${counter}`);
if(counter % 2 === 0) {
return () => console.log(`Cleanup ${counter}`);
}
}, [counter]);
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCount(counter + 1)}>Inc</button>
</div>
);
};
ReactDOM.render(
<Demo />,
root
);
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Sometimes, nesting the cleanup function inside a condition might be confusing. Another option is to always return a cleanup function, and put the logic inside it:
useEffect(()=>{
if(foo) {
// do something
}
return () => {
if(foo) {
// cleanup something
}
}
}, [foo])
And you can see in this example, the result is the same:
const { useState, useEffect } = React;
const Demo = () => {
const [counter, setCount] = useState(0);
useEffect(() => {
console.log(`Effect ${counter}`);
return () => {
if(counter % 2 === 0) {
console.log(`Cleanup ${counter}`);
}
};
}, [counter]);
return (
<div>
<p>Counter: {counter}</p>
<button onClick={() => setCount(counter + 1)}>Inc</button>
</div>
);
};
ReactDOM.render(
<Demo />,
root
);
.as-console-wrapper { top: 0; left: 50% !important; max-height: unset !important; }
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>