Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'React Hook useEffect has a missing dependency' warning with function

Tags:

So I have this React component which uses the useEffect() hook:

const [stateItem, setStateItem] = useState(0);

useEffect(() => {
  if (condition) {
    myFunction();
  }
}, [stateItem]);

const myFunction = () => {
  return 'hello';
}

React gives me a warning about 'myFunction' being a missing dependency. I (think I) understand why and I've read through many similar questions asking for more or less the same thing, but the answer is always 'move your function into the useEffect hook'. This would be fine if not for myFunction being called from different places, e.g.:

...
return (
  <Button onClick={() => myFunction()} />
);

therefore I cannot put my function inside the useEffect hook.

One answer to a similar question was to put the function outside the component, but that would require me to pass a lot of data to my functions, e.g. const myFunction(stateItem, setStateItem, someProp) => { stuff };

which gets extremely tedious when there are several functions with many props, state hooks etc. to pass.

Aside from putting a linter ignore comment above the useEffect hook, is there anything more practical to do about this? I'm finding these things to make using react hooks very impractical.

like image 610
paddotk Avatar asked Nov 29 '19 10:11

paddotk


1 Answers

I've had issues with this.

React is always trying to keep your effects up-to-date. If you don't pass a dependency array, React will run that effect after every render, just in case.

This will run on every render

useEffect(()=> {
  // DO SOMETHING
});

If you pass an empty array, you're basically telling that your effect does not depend on anything, and it's safe to run it only once.

This will only run once

useEffect(()=> {
  // DO SOMETHING
},[]);

If you populate the dependency array, you're telling the your effect depend on those specific things, and if any of them changes, the effect needs to be run again, otherwise, it doesn't have to.

This will only run if someProp or someFunction changes.

useEffect(()=> {
  // DO SOMETHING
},[someProp,someFuction]);

NOTE: Remember functions, objects and arrays are compared by reference

So, basically your options are:

  • Move the function to the effect's body.
  • Add it do the dependency array

If you choose to add it to the array, you need to decide the following:

If that function gets modified, do you need your effect to run again?

If this is true, simply add it to the dependency array and React will take care of re-running your effect every time that function changes.

If this is not true, wrap your function into a useCallback so you can keep its reference the same across renders. You can also add a dependency array to the useCallback to control when the function needs to be recreated, or not.

EXTRA The function needs to be recreated, but you don't want to re-run.

  • Add some variable using useRef() to keep track of whether the effect has run once, and write a check into your effect to stop the effect if it's has run before. Like:
const effectHasRun_ref = useRef(false);
useEffect(()=>{
  if (effectHasRun_ref.current === true) {
    return;
  }
  else {
    // RUN YOUR EFFECT
    effectHasRun_ref.current = true;
  }
},[yourFunction]);
like image 190
cbdeveloper Avatar answered Sep 29 '22 09:09

cbdeveloper