Skip to content

Commit 6e3a120

Browse files
committed
[eslint] Disallow usage within try/catch blocks
Follow up to #34032. The linter now ensures that `use` cannot be used within try/catch.
1 parent 9be531c commit 6e3a120

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,34 @@ const allTests = {
13241324
`,
13251325
errors: [asyncComponentHookError('use')],
13261326
},
1327+
{
1328+
code: normalizeIndent`
1329+
function App({p1, p2}) {
1330+
try {
1331+
use(p1);
1332+
} catch (error) {
1333+
console.error(error);
1334+
}
1335+
use(p2);
1336+
return <div>App</div>;
1337+
}
1338+
`,
1339+
errors: [tryCatchUseError('use')],
1340+
},
1341+
{
1342+
code: normalizeIndent`
1343+
function App({p1, p2}) {
1344+
try {
1345+
doSomething();
1346+
} catch {
1347+
use(p1);
1348+
}
1349+
use(p2);
1350+
return <div>App</div>;
1351+
}
1352+
`,
1353+
errors: [tryCatchUseError('use')],
1354+
},
13271355
],
13281356
};
13291357

@@ -1383,7 +1411,7 @@ if (__EXPERIMENTAL__) {
13831411
const onEvent = useEffectEvent((text) => {
13841412
console.log(text);
13851413
});
1386-
1414+
13871415
useEffect(() => {
13881416
onEvent('Hello world');
13891417
});
@@ -1421,7 +1449,7 @@ if (__EXPERIMENTAL__) {
14211449
});
14221450
return <Child onClick={() => onClick()} />
14231451
}
1424-
1452+
14251453
// The useEffectEvent function shares an identifier name with the above
14261454
function MyLastComponent({theme}) {
14271455
const onClick = useEffectEvent(() => {
@@ -1573,6 +1601,12 @@ function asyncComponentHookError(fn) {
15731601
};
15741602
}
15751603

1604+
function tryCatchUseError(fn) {
1605+
return {
1606+
message: `React Hook "${fn}" cannot be called in a try/catch block.`,
1607+
};
1608+
}
1609+
15761610
// For easier local testing
15771611
if (!process.env.CI) {
15781612
let only = [];

packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77
/* eslint-disable no-for-of-loops/no-for-of-loops */
88

99
import type {Rule, Scope} from 'eslint';
10-
import type {CallExpression, DoWhileStatement, Node} from 'estree';
10+
import type {
11+
CallExpression,
12+
CatchClause,
13+
DoWhileStatement,
14+
Node,
15+
TryStatement,
16+
} from 'estree';
1117

1218
// @ts-expect-error untyped module
1319
import CodePathAnalyzer from '../code-path-analysis/code-path-analyzer';
@@ -111,6 +117,18 @@ function isInsideDoWhileLoop(node: Node | undefined): node is DoWhileStatement {
111117
return false;
112118
}
113119

120+
function isInsideTryCatch(
121+
node: Node | undefined,
122+
): node is TryStatement | CatchClause {
123+
while (node) {
124+
if (node.type === 'TryStatement' || node.type === 'CatchClause') {
125+
return true;
126+
}
127+
node = node.parent;
128+
}
129+
return false;
130+
}
131+
114132
function isUseEffectEventIdentifier(node: Node): boolean {
115133
if (__EXPERIMENTAL__) {
116134
return node.type === 'Identifier' && node.name === 'useEffectEvent';
@@ -532,6 +550,16 @@ const rule = {
532550
continue;
533551
}
534552

553+
// Report an error if use() is called inside try/catch.
554+
if (isUseIdentifier(hook) && isInsideTryCatch(hook)) {
555+
context.report({
556+
node: hook,
557+
message: `React Hook "${getSourceCode().getText(
558+
hook,
559+
)}" cannot be called in a try/catch block.`,
560+
});
561+
}
562+
535563
// Report an error if a hook may be called more then once.
536564
// `use(...)` can be called in loops.
537565
if (

0 commit comments

Comments
 (0)