Nic Bernard
Nic Bernard

Reputation: 65

useEffect is missing a dependency, but when I add it I get a 'maximum update depth exceeded' error

I'm trying to wrap my head around using hooks and I'm running into a repeating problem. In the example I've linked below, I have a useEffect hook that's sorting an array. The sorting order is determined by a state value and I have a button that toggles that state value.

Everything is working that way I intended, the array is sorted when the component mounts, and then on the button click.

However, I'm getting a error from the linter that the array of values needs to be declared as a dependency in the useEffect hook. If I do that, I get a 'Maximum update depth exceeded' error. I'm unsure what to do, and I would appreciate any help!

Link to code

Thanks for taking a look, it really means a lot to me!

Upvotes: 6

Views: 765

Answers (3)

Asciant
Asciant

Reputation: 2160

Ideally, you would add some condition/check to see if values already has the value that you want. As setValues appears to be called on every render, which triggers a re-render, times infinity.

Potentially, using something like Lodash's isEqual combined with Lodash's orderBy would do this trick (because sort will mutate the original array).

  const [ascValue, setAscValue] = useState(true);
  const [values, setValues] = useState([
    { id: 10 },
    { id: 5 },
    { id: 12 },
    { id: 1 },
    { id: 2 },
    { id: 900 },
    { id: 602 }
  ]);

  useEffect(() => {
    function sortValues() {
      const sorter = ascValue ? "asc" : "desc";
      if (!_.isEqual(values, _.orderBy(values, ["id"], [sorter]))) {
        console.log("logged");
        setValues(() => _.orderBy(values, ["id"], [sorter]));
      }
    }

    sortValues();
  }, [ascValue, values]);

The point is, useEffect shouldn't set state every on every render by default (it can once render has finished and the user triggers an action).

A fork of your CodeSandbox to demonstrate that the side effect should only update the components state if the condition is met.

Upvotes: 1

Mateusz Falkowski
Mateusz Falkowski

Reputation: 436

It's not suprising - when You adding the values list in the dependency array, the useEffect will execute on every change of the value list - that leads to the infinite loop :)

According to the previous answer of @norbitrial, hiding of the error will not resolve the things, becuase every dependency from scope MUST be declared in the dependencies array, avoiding it can lead to unexpected behaviours.

I would recommend to consider using useReducer in this situation. It will cause that logic of modifications of the dependency array can be moved out of the useEffect hooks' scope - and You will be not forced to attach it as a dependency of useEffect.

Upvotes: 0

norbitrial
norbitrial

Reputation: 15166

I don't see any issues with your code. What I would do is just simply ignore the warning by adding // eslint-disable-next-line react-hooks/exhaustive-deps as the following:

useEffect(() => {
    function sortValues() {
      let sorted;
      const array = [...values];
      if (ascValue) {
        sorted = array.sort((a, b) => a - b);
      } else {
        sorted = array.sort((a, b) => b - a);
      }

      setValues(sorted);
    }

    sortValues();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ascValue]);

Maybe one shortening on sortValues():

function sortValues() {
    const compare = ascValue ? (a, b) => a - b : (a, b) => b - a;
    setValues([...values].sort(compare));
}

I hope that helps!

Upvotes: 2

Related Questions