Its not an error but a warning that can save you from bugs because of useEffect
hook not running when it was supposed to.
useEffect
hook, by default, executes after:
- the initial render
- each time a component is re-rendered
Sometimes we don't want this default behavior; passing a second optional argument to useEffect
hook changes the default execution behavior of useEffect
hook. Second argument to useEffect
hook is known as its dependency array that tells React when to execute the useEffect
hook.
Run "useEffect" once, after the initial render
We can achieve this by passing an empty array as the second argument to the useEffect
hook:
useEffect(() => {
// code
}, []);
This effect will only execute once, similar to componentDidMount
in class components.
Run "useEffect" everytime any of its dependency changes
When the code inside the useEffect
depends on the state or a prop, you sometimes want useEffect
to execute every time that piece of state or prop changes.
How can we tell React to run the effect every time a particular state or prop changes? By adding that state or prop in the dependency array of the useEffect
hook.
Example:
Imagine a Post
component that receives post id as a prop and it fetches the comments related to that post.
You might write the following code to fetch the comments:
useEffect(() => {
fetch(`/${props.postId}`)
.then(res => res.json())
.then(comments => setComments(comments))
.catch(...)
}, []);
Problem with the above code:
When the Post
component is rendered for the first time, useEffect
hook will execute, fetching the comments using the id of the post passed in as the argument.
But what if the post id changes or the post id is not available during the first render of the Post
component?
If post id prop changes, Post
component will re-render BUT the post comments will not be fetched because useEffect
hook will only execute once, after the initial render.
How can you solve this problem?
By adding post id prop in the dependency array of the useEffect
hook.
useEffect(() => {
fetch(`/${props.postId}`)
.then(res => res.json())
.then(comments => setComments(comments))
.catch(...)
}, [props.postId]);
Now every time post id changes, useEffect
will be executed, fetching the comments related to the post.
This is the kind of problem you can run into by missing the dependencies of the useEffect
hook and React is warning you about it.
You should not omit any dependencies of the useEffect
hook or other hooks like: useMemo
or useCallback
. Not omitting them will save you from such warnings from React but more importantly, it will save you from bugs.
Infinite loop of state update and re-render
One thing to keep in mind when adding dependencies in the dependency array of the useEffect
is that if your are not careful, your code can get stuck in an infinite cycle of:
useEffect --> state update ---> re-render --> useEffect ....
Consider the following example:
useEffect(() => {
const newState = state.map(...);
setState(data);
}, [state, setState]);
In the above example, if we remove the state
from the dependency array, we will get a warning about missing dependencies and if we add state
in the array, we will get an infinite cycle of state update and re-render.
What can we do?
One way is to skip the state
as a dependency of the useState
hook and disable the warning using the following:
// eslint-disable-next-line react-hooks/exhaustive-deps
Above solution will work but it's not ideal.
Ideal solution is to change your code in such a way that allows you to remove the dependency that is causing the problem. In this case, we can simply use the functional form of the setState
which takes a callback function as shown below:
useEffect(() => {
setState(currState => currState.map(...));
}, [setState]);
Now we don't need to add state
in the dependency array - problem solved!
Summary
- Don't omit the dependencies of the
useEffect
hook
- Be mindful of the infinite cycle of state update and re-render. If you face this problem, try to change your code in such a way that you can safely remove the dependency that is causing the infinite cycle