Reputation: 65
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!
Thanks for taking a look, it really means a lot to me!
Upvotes: 6
Views: 765
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
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
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