useEffect
useEffect() is an escape hatch for synchronizing with external systems (APIs, DOM manipulation, third-party libraries, browser APIs).
It should not be used for orchestrating the state. React linters will warn against that.
useEffect() documentation | You Might Not Need an Effect
Dependency Patterns
With dependencies - runs after the initial render and when dependencies change:
useEffect(() => {
fetchData(userId);
}, [userId]);
No dependencies - runs after every render:
useEffect(() => {
console.log('Runs on every render');
});
Empty array - runs once after initial mount:
useEffect(() => {
const connection = connectToServer();
}, []);
Cleanup Function
Return a function from useEffect() to clean up side effects. It runs before the effect re-executes and when the component unmounts.
Cleanup functions capture state/props from the previous render (old scope).
useEffect(() => {
const subscription = api.subscribe(id);
return () => {
subscription.unsubscribe();
};
}, [id]);
Common cleanup use cases: clearing timers, canceling requests, unsubscribing from events.
Dev Strict Mode
In development, React runs effects twice to help find bugs. This only happens in Strict Mode.
useEffect(() => {
console.log('You should see me twice in dev mode');
return () => console.log('And me in between!');
}, []);
useEffectEvent()
Allows defining event handlers that always have access to the latest state and props without needing to include them in the dependency array.
const onVisit = useEffectEvent((url) => {
logAnalytics(url, shoppingCart);
});
useEffect(() => {
onVisit(url);
}, [url]);
Timeline
There could be more nuances to this, but generally the order of things is as follows:
- Component render logic
- React commits to DOM
useLayoutEffect()cleanupuseLayoutEffect()effect- Browser paints
useEffect()cleanupuseEffect()effect
Important notes:
useLayoutEffect()runs before the browser paints, blocking visual updates until it’s done.- Cleanup functions capture state/props from the previous render (old scope).