Skip to content

Commit 7c4a7c9

Browse files
authored
react-hooks/rules-of-hooks: Improve support for do/while loops (#31720)
1 parent 16367ce commit 7c4a7c9

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js

+12
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,18 @@ const tests = {
550550
// TODO: this should error but doesn't.
551551
// errors: [genericError('useState')],
552552
},
553+
{
554+
code: normalizeIndent`
555+
// Valid because the hook is outside of the loop
556+
const Component = () => {
557+
const [state, setState] = useState(0);
558+
for (let i = 0; i < 10; i++) {
559+
console.log(i);
560+
}
561+
return <div></div>;
562+
};
563+
`,
564+
},
553565
],
554566
invalid: [
555567
{

packages/eslint-plugin-react-hooks/src/RulesOfHooks.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ function isInsideComponentOrHook(node) {
100100
return false;
101101
}
102102

103+
function isInsideDoWhileLoop(node) {
104+
while (node) {
105+
if (node.type === 'DoWhileStatement') {
106+
return true;
107+
}
108+
node = node.parent;
109+
}
110+
return false;
111+
}
112+
103113
function isUseEffectEventIdentifier(node) {
104114
if (__EXPERIMENTAL__) {
105115
return node.type === 'Identifier' && node.name === 'useEffectEvent';
@@ -295,7 +305,7 @@ export default {
295305
if (pathList.has(segment.id)) {
296306
const pathArray = Array.from(pathList);
297307
const cyclicSegments = pathArray.slice(
298-
pathArray.indexOf(segment.id) - 1,
308+
pathArray.indexOf(segment.id) + 1,
299309
);
300310
for (const cyclicSegment of cyclicSegments) {
301311
cyclic.add(cyclicSegment);
@@ -485,7 +495,10 @@ export default {
485495
for (const hook of reactHooks) {
486496
// Report an error if a hook may be called more then once.
487497
// `use(...)` can be called in loops.
488-
if (cycled && !isUseIdentifier(hook)) {
498+
if (
499+
(cycled || isInsideDoWhileLoop(hook)) &&
500+
!isUseIdentifier(hook)
501+
) {
489502
context.report({
490503
node: hook,
491504
message:
@@ -520,7 +533,8 @@ export default {
520533
if (
521534
!cycled &&
522535
pathsFromStartToEnd !== allPathsFromStartToEnd &&
523-
!isUseIdentifier(hook) // `use(...)` can be called conditionally.
536+
!isUseIdentifier(hook) && // `use(...)` can be called conditionally.
537+
!isInsideDoWhileLoop(hook) // wrapping do/while loops are checked separately.
524538
) {
525539
const message =
526540
`React Hook "${getSource(hook)}" is called ` +

0 commit comments

Comments
 (0)