Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Hooks useEffect, adding dependency triggers infinite loop

Inside of my useEffect, I have a props dependency (setIsValid). When I add this dependency to the useEffect, it lands in an infinite loop.

Parent when Calling Child Component:

const setIsValid = (bool) => {
  const tmpStateCopy = Object.assign({}, state);
  tmpStateCopy.isValid = bool;

  setState(tmpStateCopy);
};

return <Child
  setIsValid={setIsValid}
/>

In the Child Component:

const { setIsValid } = props;

const [state, setState] = useState({
  transformations: [],
  duplicateIndexes: []
});

const { transformations, duplicateIndexes } = state;

useEffect(() => {
  const invalids = transformations.find(x => (x.value === '' || x.replaceWith === ''));
  const hasDuplicates = duplicateIndexes.length > 0;
  const isValid = ((invalids === undefined) && (transformations.length > 0) && !hasDuplicates);

  setIsValid(isValid)

  console.log('got triggered');
}, [state]);

This way the code works but I always get a warning.

What I want is, that the validation is always triggered when one of the values inside the state changes (transformations / duplicateIndexes).

By adding the setIsValid() func from the props, it runs infinitely.

The Warning looks like this:

./src/components/UI/integrationBuilder/layoutElements/transformer/modules/ifModules/ifModule.js
  Line 103:  React Hook useEffect has missing dependencies: 'duplicateIndexes.length', 'setIsValid', and 'transformations'. Either include them or remove the dependency array  react-hooks/exhaustive-deps

My question is, how can I keep the same logic without getting this warning?

like image 259
Emre Avatar asked Jun 11 '19 12:06

Emre


People also ask

How do you fix the infinite loop inside useEffect React hooks?

To get rid of your infinite loop, simply use an empty dependency array like so: const [count, setCount] = useState(0); //only update the value of 'count' when component is first mounted useEffect(() => { setCount((count) => count + 1); }, []); This will tell React to run useEffect on the first render.

What causes infinite loops in React?

By executing a function before setting a handler, you update a state inside the render, which causes an infinite loop.

Why does my useEffect keep running?

Changing state will always cause a re-render. By default, useEffect always runs after render has run. This means if you don't include a dependency array when using useEffect to fetch data, and use useState to display it, you will always trigger another render after useEffect runs.

How do I make useEffect run forever?

useEffect(() => setCount(count + 1)); it generates an infinite loop of component re-renderings.


Video Answer


1 Answers

Since, when state changes you will call the effect. transformations and duplicateIndexes will already be considered for. To avoid the warning, you can move the destructure within useEffect

const { setIsValid } = props;

const [state, setState] = useState({
  transformations: [],
  duplicateIndexes: []
});



useEffect(() => {
  const { transformations, duplicateIndexes } = state;
  const invalids = transformations.find(x => (x.value === '' || x.replaceWith === ''));
  const hasDuplicates = duplicateIndexes.length > 0;
  const isValid = ((invalids === undefined) && (transformations.length > 0) && !hasDuplicates);

  setIsValid(isValid)

  console.log('got triggered');
}, [state]);

Also regarding setIsValid as a dependency to useEffect, you must not do that since a new function for it is created on every render and it will cause the useEffect to to run again and again unles you refactor your code a bit.

const setIsValid = useCallback((bool) => {
  setState(prev =>  Object.assign({}, prev, {isValid: bool});
}, []);

and now you can set setIsValid as a dependency.

like image 63
Shubham Khatri Avatar answered Sep 30 '22 14:09

Shubham Khatri