react: Bug: Unexpected warning in react-hooks/exhaustive-deps using optional chaining operator

When the optional chaining operator (?.) is used inside a hook and the variable is already listed in the dependencies eslint-plugin-react-hooks reports an unexpected “missing dependency” warning. Replacing ?. with . produces the expected behaviour: the warning is no longer reported.

I would expect that the optional chaining operator (?.) be handled as normal property access (.) in the context of this validation.

React version: 16.13.1 eslint-plugin-react-hooks version: 4.0.2

Steps To Reproduce

Notice that foo is both referenced inside the code in useEffect and is listed as a dependency.

import React, {useEffect} from 'react';

const getFoo = () => undefined;
const doSomethingWith = (foo) => {};

export default () => {
  const foo = getFoo();

  useEffect(() => {
    if (foo?.bar) {
      doSomethingWith(foo);
    }
  }, [foo]);

  return null;
};
package.json
{
  "name": "react-hooks-bug",
  "version": "1.0.0",
  "description": "",
  "license": "ISC",
  "scripts": {
    "test": "eslint test.js"
  },
  "dependencies": {
    "babel-eslint": "^10.1.0",
    "eslint": "^7.1.0",
    "eslint-plugin-react-hooks": "^4.0.2"
  },
  "eslintConfig": {
    "parser": "babel-eslint",
    "parserOptions": {
      "sourceType": "module"
    },
    "plugins": [
      "react-hooks"
    ],
    "rules": {
      "react-hooks/exhaustive-deps": "warn"
    }
  }
}

The current behavior

The following warning is reported:

warning React Hook useEffect has a missing dependency: 'foo?.bar'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

The expected behavior

No warning should be reported.

Additional notes

With the following code, which only replaces ?. with ., eslint-plugin-react-hooks behaves as expected, that is no warning is reported. (Of course this fails at runtime when foo is undefined).

import React, {useEffect} from 'react';

const getFoo = () => undefined;
const doSomethingWith = (foo) => {};

export default () => {
  const foo = getFoo();

  useEffect(() => {
    if (foo.bar) {
      doSomethingWith(foo);
    }
  }, [foo]);

  return null;
};

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 9
  • Comments: 19 (3 by maintainers)

Commits related to this issue

Most upvoted comments

eslint-plugin-react-hooks@4.0.4

@tbergq If I make that change it will miss changes to foo.

Sorry, yes, you are right, I missed the doSomethingWith(foo);

The point that I’m trying to make is that the optional chaining operator (?.) should be handled exactly as normal property access (.) in the context of this validation.

Yeah, that is probably right, I see if you write this without optional chaining it would look like

export default () => {
  const foo = getFoo();

  React.useEffect(() => {
    if (foo && foo.bar) {
      doSomethingWith(foo);
    }
  }, [foo]);

  return null;
};

and that does not produce an eslint error.

I’m hitting this too with latest exhaustive-deps

Yeah, we are getting the same bug as well. Any hints on how to solve the problem? : ) Thank you!

Update:

This was just fixed in https://github.com/facebook/react/pull/19062 😹 Thank you so much!

@yanneves You’re right it’s basically the same check. Thank you for checking.

If it ever happens again I’ll know pretty soon after 😃

Happy to take a fix!