Home > database >  React useEffect: Prevent crashing of app when updating state that is required in the dependency arra
React useEffect: Prevent crashing of app when updating state that is required in the dependency arra

Time:02-28

I am in a situation where I need to update a certain state in the useEffect, while depending on the value of the state itself, and had to put it in the dependency array - due to a warning from eslint-react-hooks. However, due to the fact that I am updating a state that is present in the dependency array, this causes the app to crash due to what I believe is an infinite loop happening in the useEffect.

The example below shows a simple example:

const [list, setList] = useState(list);

useEffect(() => {
  const currentList = [...list];
  const newList = currentList.push(newItem);

  setList(newList);
}, [newItem, list]);

I have came up with a solution to use the previous state argument in the set state method of the useState hook to update accordingly. This effectively removes the need for the dependency in the dependency array.

const [list, setList] = useState(list);

useEffect(() => {
  setList((prevList) => {
    const currentList = [...prevList];
    const newList = currentList.push(newItem);

    return newList;
  });
}, [newItem]);

I would like to ask if anyone have faced this issue before, and have found a better solution? Thank you!

CodePudding user response:

The general approach of using the setter's callback instead of having to refer to the outer state value is perfectly normal and acceptable. You can slim it down too:

useEffect(() => {
  setList(prevList => [...prevList, newItem]);
}, [newItem]);

But it's a little bit strange to have a separate newItem state that immediately gets merged into the list. Consider if you could merge them together so you only have one stateful variable in the end. Instead of doing

setNewItem(theNewItem);

do

setList(prevList => [...prevList, theNewItem]);

and then, wherever else you refer to newItem, change it to list[list.length - 1] - or do

const newItem = list[list.length - 1];

in the body of the component.

That approach won't work for all situations, but consider whether it'd be applicable to yours.

CodePudding user response:

You can keep the list unchanged and use another state to update the ui:

const [list, setList] = useState(list);
const [count, setCount] = useState(list.length);

useEffect(() => {
  list.push(newItem);
  setList(list);
  setCount(list.length);
}, [newItem, list]);
  • Related