Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
jcestibariz opened this issue May 23, 2020 · 19 comments

Comments

@jcestibariz
Copy link

jcestibariz commented May 23, 2020

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;
};
@jcestibariz jcestibariz added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label May 23, 2020
@Mostafa-Mohammadi
Copy link

what is bar ?

@jcestibariz
Copy link
Author

This is not real code it's just a synthetic example to reproduce this particular bug. In this example bar would be a possible property of foo.

@Mostafa-Mohammadi
Copy link

Mostafa-Mohammadi commented May 23, 2020

This is not real code it's just a synthetic example to reproduce this particular bug. In this example bar would be a possible property of foo.

By using the ?. operator instead of just ., JavaScript knows to implicitly check to be sure foo.bar is not null or undefined before attempting to access bar. If foo is null or undefined, the expression automatically short-circuits, returning undefined.

just put your foo.bar in deps Array useEffect

@deluksic
Copy link

I'm hitting this too with latest exhaustive-deps

@tbergquist-godaddy
Copy link

From looking at this change, it looks like you need to change your code to

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

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

  return null;
};

And it should work.

@jcestibariz
Copy link
Author

jcestibariz commented May 25, 2020

@tbergq If I make that change it will miss changes to 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.

@tbergquist-godaddy
Copy link

@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.

@belliAndrea
Copy link

Got the same problem here.

@yanneves
Copy link
Contributor

This also happens in cases where we may access a prototype method on the dependency, such as

React.useEffect(() => {
  if (arr?.includes('value')) {
    // ...
  }
}, [arr])

@gaearon
Copy link
Collaborator

gaearon commented May 26, 2020

Happy to take a fix!

@gaearon gaearon added Component: ESLint Rules Type: Bug and removed Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug labels May 26, 2020
yanneves added a commit to yanneves/react that referenced this issue May 26, 2020
@yanneves
Copy link
Contributor

@gaearon addressed in #19008 - first time digging into eslint plugin code, comments welcome 👀

@jcestibariz
Copy link
Author

@yanneves Looks like that would work, do you mind adding a test case for this specific issue too? Something like:

{
  code: normalizeIndent`
    function MyComponent(props) {
      useEffect(() => {
        console.log(props?.foo);
        console.log(props);
      }, [props]);
    }
  `
}

gaearon pushed a commit that referenced this issue May 27, 2020
… and optional chaining of dependencies, relates to #18985 (#19008)
@gaearon
Copy link
Collaborator

gaearon commented May 27, 2020

eslint-plugin-react-hooks@4.0.4

@yanneves
Copy link
Contributor

@jcestibariz just added that to the test suite and it passes, not sure whether it's worth submitting a new PR for it, the underlying logic is identical to this test

{
  code: normalizeIndent`
    function MyComponent(props) {
      useEffect(() => {
        console.log(props.foo);
        console.log(props.foo?.bar);
      }, [props.foo]);
    }
  `,
}

@jcestibariz
Copy link
Author

@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 :)

@wojtekmaj
Copy link

With 4.0.4, I'm still getting an error at

const computedArray = useMemo(
  () => veryLongArray?.map(costlyComputation), 
  [veryLongArray]
);

Exact error:

4:2 warning  React Hook useMemo has an unnecessary dependency: 'veryLongArray'. Either exclude it or remove the dependency array  react-hooks/exhaustive-deps

@belliAndrea
Copy link

me too

@gabrieledarrigo
Copy link

gabrieledarrigo commented Jun 30, 2020

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 #19062 😹
Thank you so much!

@gaearon
Copy link
Collaborator

gaearon commented Jul 7, 2020

Optional chaining problems should be fixed in eslint-plugin-react-hooks@4.0.6. If you experience some problem with optional chaining after 4.0.6, please file a new issue. Thanks.

@facebook facebook locked as resolved and limited conversation to collaborators Jul 7, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants