Use functions as useEffect dependencies -> "dependency 'props' missing"

Viewed 1724

My intention is to use a function (received by a parent component, created with useCallback) as dependency in useEffect and only trigger that effect if the function changes.

Consider the following simple component:

function MyComponent(props) {
  const [myState, setMyState] = useState(0);

  useEffect(() => {
    setMyState(props.calculate(1));
  }, [props.calculate]) 

  return <h1>{myState}</h1>;
}

It receives a function calculate in props and computes myState from it. However I get the linting error:

React Hook useEffect has a missing dependency: 'props'. Either include it or remove the dependency array.

I don't understand why props.calculate is not sufficient as a dependency and we need props. I don't want to use props because then the effect would retrigger on every render, even if calculate did not change. (Assume calculate has been created using useCallback in parent).

The following version does not get any linting errors:

function MyComponentVersion2(props) {
  const { calculate } = props;
  const [myState, setMyState] = useState(0);

  useEffect(() => {
    setMyState(calculate(1));
  }, [calculate]) 

  return <h1>{myState}</h1>;
}

My questions are:

  • Why is the linter not fine with MyComponent?
  • Are there semantical differences in MyComponent compared to MyComponentVersion2?

I made a minimal example here in this codesandbox

Thank you!

1 Answers

It's because the call props.calculate(1) implicitly passes props as the this value to calculate, so technically the result may depend on the entire props object (see this React issue). For the deconstructed calculate call, this will be undefined or the global object, so there's no dependency on props.

You can see the difference between the two components by replacing (x) => {return x + 1;} with function (x) {console.log(this); return x + 1;} in the definition of calculate.

Related