Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a state variable's setter needed as a dependency with useEffect when passed in through props?

Compare the following two components:

Child.js

import React, { useEffect } from "react";

function Child({ count, setCount }) { // Note: Has parameter
  useEffect(() => {
    setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
  }, []);

  return <div>{count}</div>;
}

export default Child;

Child2.js

import React, { useEffect, useState } from "react";

function Child2() { // Note: No parameter
  const [count, setCount] = useState(0); // State variable assigned in component

  useEffect(() => {
    setInterval(() => {
      setCount(prevCount => prevCount + 1);
    }, 1000);
  }, []);

  return <div>{count}</div>;
}

export default Child2;

They are essentially the same. The difference between the two is that Child.js gets the state variable count and its setter setCount passed in from its parent, while Child2.js sets that state variable itself.

They both work fine, but Child.js (and only Child.js) complains about "a missing dependency: 'setCount'." Adding setCount to the dependencies array makes the warning go away, but I'm trying to figure out why that's necessary. Why is the dependency required in Child but not Child2?

I have working examples at https://codesandbox.io/s/react-use-effect-dependencies-z8ukl.

like image 600
Webucator Avatar asked Dec 05 '19 15:12

Webucator


1 Answers

The ESLint rule is not extremely intelligent to determine what may or maynot change and thus prompts you to pass every variable that is being used within the useEffect function callback so that you don't miss those changes accidently.

Since hooks are heavily dependent on closure, and beginner programmers find it difficult to debug problems related to closures, eslint warning here serves as a good help to avoid such cases.

Now since state updater is directly returned from useState and doesn't change during the course of your component Lifecycle you can avoid passing it as a dependency and disable the warning like

useEffect(() => {
   setInterval(() => {
      setCount(prevCount => prevCount + 1);
   }, 1000);
   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
like image 100
Shubham Khatri Avatar answered Oct 21 '22 12:10

Shubham Khatri