The return value of the useEffect callback is an effect cleanup function which is called before the effect callback and before the component unmounts.
I'm asking myself whether it can be guarranteed that the cleanup function will synchronously be called right before its corresponding effect callback function? Given that the cleanup function is itself synchronous of course.
Example: In this simple component here the string "cleanup" is indeed logged before "effect" each time you hit the Increment button.
export default function App() {
const [count, setCount] = useState(0);
console.log("App: render");
useEffect(() => {
console.log("effect");
return () => console.log("cleanup");
}, [count]);
return (
<>
<h2>useEffect</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</>
);
}
But can this order always be guaranteed? In fact: Can you be sure that the cleanup code runs directly before the corresponding effect function?
Or is there perhaps any async behavior involved in the cleanup function of useEffect?
No, not in general. You can't generally guarantee that the cleanup function of a given hook will run right before its corresponding effect function.
But that's not the case because of any hidden async behavior involved (unless you explicitly create an async cleanup function.
In your code example you can indeed guarantee the cleanup function running directly before the effect function in case the component gets updated. But that's just because you only have a single component with a single useEffect function.
If you have several useEffect hooks in one component or other child components with useEffect hooks, all cleanup functions of all components are run before all effect functions of all useEffect hooks are run:
Ass @keith mentioned in his comment the order of execution is:
render, render, cleanup, cleanup, effect, effect
And not
render, render, cleanup, effect, cleanup, effect
In this CodeSandBox you see exactly that and can play around with it.