Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React hook useEffect dependency array

I trying to wrap my head around the new hooks api of react. Specifically, I'm trying to construct the classic use case that once was the following:

componentDidUpdate(prevProps) {     if (prevProps.foo !== this.props.foo) {         // animate dom elements here...         this.animateSomething(this.ref, this.props.onAnimationComplete);     } } 

Now, I tried to build the same with a function component and useEffect, but can't figure out how to do it. This is what I tried:

useEffect(() => {     animateSomething(ref, props.onAnimationComplete); }, [props.foo]); 

This way, the effect is only called when props.foo changes. And that does work – BUT! It appears to be an anti-pattern since the eslint-plugin-react-hooks marks this as an error. All dependencies that are used inside the effect should be declared in the dependencies array. So that means I would have to do the following:

useEffect(() => {     animateSomething(ref, props.onAnimationComplete); }, [props.foo, ref, props.onAnimationComplete]); 

That does not lead to the linting error BUT it totally defeats the purpose of only calling the effect when props.foo changes. I don't WANT it to be called when the other props or the ref change.

Now, I read something about using useCallback to wrap this. I tried it but didn't get any further.

Can somebody help?

like image 431
overdub60 Avatar asked Mar 18 '19 18:03

overdub60


People also ask

Does useEffect work with arrays?

This array will re-run useEffect, if the values inside it changes. This will work perfectly fine when the values passed in the dependency array are of type boolean, string or numbers. But it will have some gotchas when you are dealing with complex values such as objects or arrays.

What is dependency list in useEffect?

useEffect(callback, dependencies) is the hook that manages the side-effects in functional components. callback argument is a function to put the side-effect logic. dependencies is a list of dependencies of your side-effect: being props or state values.

What does useEffect () hook do in React?

What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we'll refer to it as our “effect”), and call it later after performing the DOM updates.

Why React hook useEffect has a missing dependency?

The warning "React Hook useEffect has a missing dependency" occurs when the useEffect hook makes use of a variable or function that we haven't included in its dependencies array. To solve the error, disable the rule for a line or move the variable inside the useEffect hook.


2 Answers

I would recommend writing this as follows:

const previousFooRef = useRef(props.foo);  useEffect(() => {     if (previousFooRef.current !== props.foo) {        animateSomething(ref, props.onAnimationComplete);        previousFooRef.current = props.foo;     } }, [props.foo, props.onAnimationComplete]); 

You can't avoid the complexity of having a condition inside the effect, because without it you will run your animation on mount rather than just when props.foo changes. The condition also allows you to avoid animating when things other than props.foo change.

By including props.onAnimationComplete in the dependencies array, you avoid disabling the lint rule which helps ensure that you don’t introduce future bugs related to missing dependencies.

Here's a working example:

Edit animate

like image 50
Ryan Cogswell Avatar answered Sep 17 '22 23:09

Ryan Cogswell


Suppress the linter because it gives you a bad advice. React requires you to pass to the second argument the values which (and only which) changes must trigger an effect fire.

useEffect(() => {     animateSomething(ref, props.onAnimationComplete); }, [props.foo]); // eslint-disable-line react-hooks/exhaustive-deps 

It leads to the same result as the Ryan's solution.

I see no problems with violating this linter rule. In contrast to useCallback and useMemo, it won't lead to errors in common case. The content of the second argument is a high level logic.

You may even want to call an effect when an extraneous value changes:

useEffect(() => {     alert(`Hi ${props.name}, your score is changed`); }, [props.score]); 
like image 41
Finesse Avatar answered Sep 20 '22 23:09

Finesse