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: eslint(react-hooks/exhaustive-deps) When a property is accessed with and without optional chaining, exhaustive-deps' code suggestion will introduce an error #23248

Open
dantman opened this issue Feb 8, 2022 · 1 comment · May be fixed by #30989
Labels
Component: ESLint Rules Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug Type: Bug

Comments

@dantman
Copy link
Contributor

dantman commented Feb 8, 2022

Steps To Reproduce

StackBlitz demo

I've enabled enableDangerousAutofixThisMayCauseInfiniteLoops so you can run npx eslint --fix index.js and see the change that
react-hooks/exhaustive-deps makes through the code suggestions API.

The current behavior

When you accept react-hooks/exhaustive-deps' code suggestions the following code:

import { useState, useEffect } from 'react';

export function MyComponent() {
  const [one] = useState(1);
  const [foo] = useState(undefined);

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

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

  return null;
}

Will be incorrectly fixed to the following.

import { useState, useEffect } from 'react';

export function MyComponent() {
  const [one] = useState(1);
  const [foo] = useState(undefined);

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

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

  return null;
}

The expected behavior

react-hooks/exhaustive-deps should be recommending the following:

import { useState, useEffect } from 'react';

export function MyComponent() {
  const [one] = useState(1);
  const [foo] = useState(undefined);

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

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

  return null;
}

Additional details

When you include both an optional chaining usage foo?.bar and a non-optional chaining usage foo.bar exhaustive-deps will use the version without the optional chaining for the deps array. i.e. It will recommend a deps array containing foo.bar, which because foo may be undefined and the deps array is outside will result in a silent runtime error in JS or a TypeScript error in TS.

If you only use foo?.bar it will recommend foo?.bar. So this may be an order of usage issue, i.e. preferring the last usage. I haven't checked. However that is a problem because the most common reason to access the same property with and without optional chaining is a case where you have an if condition which implicitly guarantees that the member you use optional chaining on is not nullish and can be accessed directly and thus do not need optional chaining within the if condition's body. And in that case the version without optional chaining will always be the latter usage.

When you are already using foo?.bar will not try to autofix that. However this is made worse of an issue because if you are already using the correct version and you accept exhaustive-deps changes to fix a different issue (you added a new unrelated variable it needs to add) exhaustive-deps will override your foo?.bar dep and turn it into foo.bar.

If exhaustive-deps sees multiple versions of the same property access, it should recommend the version with the most optional chaining usage. Since anything less would create an error which would result in the optional chaining usage never even running.

@Tsymalyi
Copy link

thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: ESLint Rules Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug Type: Bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants