3

I am trying updating data in dispatch in useEffect, but it shows a warning in console

React Hook useEffect has missing dependencies: 'dispatch', 'id', and 'state.selectedHotel'. Either include them or remove the dependency array react-hooks/exhaustive-deps

Code:

import { GlobalContext } from "../../../context/globalContext";
const HotelDetail = () => {
    const [state, dispatch] = useContext(GlobalContext);
    const { id } = useParams();

   useEffect(() => {
       const hotelData = async () => {
          try {
              let response = await ServiceGetAllHotels();
              let hotel = response.hotels.filter(hotel => {
                    return hotel.hotelUserName === id;
              });
              dispatch({
                  type: "UPDATE",
                  payload: { selectedHotel: hotel[0] }
              });
          }catch(){}
       };
   }, [])
};

The warning message disappears when I add this:

useEffect(() => {
.....
}, [dispatch, state.selectedHotel, id])

I don't understand why this error/warning, why does it disappear when I add this?

6
  • reactjs.org/docs/… Commented Jun 24, 2021 at 7:49
  • 5
    I'm confused too. The error/warning tells you what to do to fix the issue, you do that and then you are wondering why it fixed the issue? What is it that you really want to know? Commented Jun 24, 2021 at 7:50
  • DO you think that my code is correct way? @FelixKling , I was experimenting to fix this issue Commented Jun 24, 2021 at 7:52
  • 2
    Does this answer your question? How to fix missing dependency warning when using useEffect React Hook? Commented Jun 24, 2021 at 7:52
  • 1
    The error says to add those values to the dependency list and that's exactly what you did. Commented Jun 24, 2021 at 7:54

2 Answers 2

5

It's 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" every time 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
Sign up to request clarification or add additional context in comments.

4 Comments

Very thanks for detailed answer , So I can go with this code [dispatch, state.selectedHotel, id]) @Yousaf
If the useEffect doesn't updates the state.selectedHotel, then yes.
But that's confusing as setState is not a value that will change, what's the significance of adding it to array?
I do it for making it clear what useEffect depends on and also for the sake of being consistent across the codebase. If it's a dependency, I add it in the dependency array, regardless of whether its value changes or not.
1

The useEffect hook accepts two arguments. The first one is a classic callback and the second one is an array of so called "dependencies".

The hook is designed to execute the callback immediately after component has been mount (after elements have been successfully added to the real DOM and references are available) and then on every render if at least one of the values in the dependencies array has changed.

So, if you pass an empty array, your callback will be executed only once during the full lifecycle of your component.

It makes sense if you think about it from a memory point of view. Each time that the component function is executed, a new callback is created storing references to the current execution context variables. If those variables change and a new callback is not created, then the old callback would still use the old values.

This is why "missing dependencies" is marked as a warning (not an error), code could perfectly work with missing dependencies, sometimes it could be also intentional. Even if you can always add all dependencies and then perform internal checks. It is a good practice to pass all your dependencies so your callback is always up to date.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.