Bug: `eslint-plugin-react-hooks` false positive with `for` loop in function body
imjordanxd opened this issue ยท 6 comments
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.
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
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 :)
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.
Looks like a duplicate of #31687
Oh my bad, you're right!