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-plugin-react-hooks false positive with for loop in function body #31900

Open
imjordanxd opened this issue Dec 24, 2024 · 5 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@imjordanxd
Copy link

imjordanxd commented Dec 24, 2024

React version: N/A
Eslint-plugin-react-hooks version: 5.1.0

Steps To Reproduce

Link to code example:

const Dots = () => {
  const count = 9;
  const [highlightIndex, updateHighlightIndex] = React.useState(0);

  React.useEffect(() => {
    const updateHighlightIndexIntervalID = setInterval(() => {
      updateHighlightIndex((i) => (i + 1) % count);
    }, 200);

    return () => {
      clearInterval(updateHighlightIndexIntervalID);
    };
  }, []);

  const dots: JSX.Element[] = [];
  for (let i = 0; i < count; i++) {
    dots.push(<span key={i} style={{opacity:i === highlightIndex ? 1 : 0.5}}>{i}</span>);
  }
  return <div>{dots}</div>;
};

The current behavior

The linter reports the following error:

ESLint: React Hook "React. useState" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.(react-hooks/ rules-of-hooks)

This is incorrect. The for loop is correctly reading a reactive variable. No hooks are called conditionally or inside a loop. The code can be rewritten to satisfy the linter but there is nothing wrong with the original code.

The expected behavior

No error is reported. Having a for loop reading a reactive variable should not report an error.

@imjordanxd imjordanxd added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Dec 24, 2024
@yajatkaul
Copy link

Sometimes, ESLint misinterprets the structure of the code, particularly when using modern JavaScript/TypeScript features.
Try to update the ESLint config or if its till not fixed use a comment to suppress it. There is nothing wrong with the code regarding this

@yacinec
Copy link

yacinec commented Dec 24, 2024

Hi @imjordanxd ,
The problem seems to lie with the eslint-plugin-react-hooks.
I'll take a look if no one is assigned to the outcome yet :)

@yacinec
Copy link

yacinec commented Dec 24, 2024

After some research, it appears that ESLint detects an error when using a classic loop (for, while and do while) in a component that contains hook (useState, useEffect...)

To overcome this problem, you can either :

  • Disable the ESLint rule react-hooks/rules-of-hooks (which I don't recommend)
  • Use Array's .map method instead to get around the problem.

The code would then look like this:

const Dots = () => {
  const count = 9;
  const [highlightIndex, updateHighlightIndex] = React.useState(0);

  React.useEffect(() => {
    const updateHighlightIndexIntervalID = setInterval(() => {
      updateHighlightIndex((i) => (i + 1) % count);
    }, 200);

    return () => {
      clearInterval(updateHighlightIndexIntervalID);
    };
  }, []);

  const dots = [...Array(count)].map((_, i) => (
    <span key={i} style={{ opacity: i === highlightIndex ? 1 : 0.5 }}>
      {i}
    </span>
  ));

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

I don't know if this is the behavior expected by eslint-plugin-react-hooks so for the moment I haven't opened a PR to try solving the problem in the codebase.

@imjordanxd
Copy link
Author

Looks like a duplicate of #31687

@yacinec
Copy link

yacinec commented Dec 25, 2024

Oh my bad, you're right!

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

No branches or pull requests

3 participants