useEffect(callback);
// Example
useEffect(() => {
console.log("executed after render phase");
return () => {
console.log("cleanup function after render");
};
});
- Runs on every component render.
- Typically used for debugging, analogous to function’s body execution on every
render:
const Component = () => {
callback()
return <></>;
};
Note: There is still a difference, in execution time (see the next note). Check this sandbox logs.
- The cleanup function runs after each render.
useEffect(callback,[]);
// Example
useEffect(() => {
const fetchUsers = async () => {
const users = await fetch();
setUsers(users);
};
fetchUsers();
console.log("called on component's mount");
return () => {
console.log("called on component's unmount");
};
}, []);
- Usually used for initializing components state by data fetching etc.
- Runs once on a component mount.
- The cleanup function will run on component’s unmount.
Gotchas:
- The callback executed after the render phase.
Remember, there is a first render and then a mount.
- Stale data due to closures
Roughly saying, most of bugs regarding
useEffect
is not knowing how closures works and not paying attention to linting warnings.
Make sure the array includes all values from the component scope that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders – note in React docs.
useEffect(callback,[arg]);
// Example
useEffect(() => {
console.log({ users });
return () => {
console.log("user value is changing");
};
}, [users]);
- Runs on change of
arg
value. - Usually used to run events on props/state change.
- Multiple dependencies can be provided:
[arg1,arg2,arg3...]
- The cleanup function runs on
arg
value change.
Gotchas:
- “On Change” refers to shallow comparison with the previous value of
arg
.
i.e compares the value of
arg
from the previous render and the current one,prevArg === arg ? doNothing() : callback()
.
-
Because in Javascript
{} === {} || [] === []
is a falsy statement, ifarg
(users
in our example) is an object, the callback will run on every render. -
Runs on mount too, since the first comparison always falsy
Additional Good to Know Points
useEffect
callbacks fired after browser’s re-paint.useEffect
callbacks executed in declaration order (like all hooks), check the example.- Every
useEffect
should have a SINGLE responsibility. - If you using a value from
useRef
, in the cleanup function, copy the value to callback’s scope beforehand.
const timeoutIdRef = useRef();
useEffect(() => {
const timeoutId = timeoutIdRef.current;
return () => {
/*
Using timeoutIdRef.current directly here is not safe
since you can't guarantee the ref to exists in this point
(especially when the component unmounts)
*/
// Should get a lint warning here
clearTimeout(timeoutIdRef.current); // BAD
// Closure on timeoutId value
clearTimeout(timeoutId); // GOOD
};
}, [arg]);
- Is it safe to use
ref.current
asuseEffect
‘s dependency when ref points to a DOM element? - Sometimes you need to run
useEffect
ONCE, on mount or on first render, those are the common patterns.
const isMounted = useRef(false);
useEffect(() => {
if (isMounted.current) {
// first mount
} else {
isMounted.current = true;
}
}, [arg]);
Keep reading:
- My additional answer explaining the
return
statement ofuseEffect
callback - A Complete Guide to
useEffect
by Dan Abramov useEffect
API- Using the effect hook – React docs