Skip to content

Commit b0533fe

Browse files
authored
fix(eslint-plugin-react-hooks): Support optional chaining when accessing prototype method inside useCallback and useMemo #19061 (#19062)
* fix(eslint-plugin-react-hooks): Support optional chaining when accessing prototype method #19061 * run prettier * Add fix for #19043
1 parent e9c1445 commit b0533fe

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

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

+47
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,24 @@ const tests = {
289289
}
290290
`,
291291
},
292+
{
293+
code: normalizeIndent`
294+
function MyComponent(props) {
295+
useMemo(() => {
296+
console.log(props.foo?.toString());
297+
}, [props.foo]);
298+
}
299+
`,
300+
},
301+
{
302+
code: normalizeIndent`
303+
function MyComponent(props) {
304+
useCallback(() => {
305+
console.log(props.foo?.toString());
306+
}, [props.foo]);
307+
}
308+
`,
309+
},
292310
{
293311
code: normalizeIndent`
294312
function MyComponent() {
@@ -1235,6 +1253,35 @@ const tests = {
12351253
},
12361254
],
12371255
invalid: [
1256+
{
1257+
code: normalizeIndent`
1258+
function MyComponent(props) {
1259+
useCallback(() => {
1260+
console.log(props.foo?.toString());
1261+
}, []);
1262+
}
1263+
`,
1264+
errors: [
1265+
{
1266+
message:
1267+
"React Hook useCallback has a missing dependency: 'props.foo?.toString'. " +
1268+
'Either include it or remove the dependency array.',
1269+
suggestions: [
1270+
{
1271+
desc:
1272+
'Update the dependencies array to be: [props.foo?.toString]',
1273+
output: normalizeIndent`
1274+
function MyComponent(props) {
1275+
useCallback(() => {
1276+
console.log(props.foo?.toString());
1277+
}, [props.foo?.toString]);
1278+
}
1279+
`,
1280+
},
1281+
],
1282+
},
1283+
],
1284+
},
12381285
{
12391286
code: normalizeIndent`
12401287
function MyComponent() {

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -1287,10 +1287,13 @@ function collectRecommendations({
12871287
function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) {
12881288
node.children.forEach((child, key) => {
12891289
const path = keyToPath(key);
1290+
// For analyzing dependencies, we want the "normalized" path, without any optional chaining ("?.") operator
1291+
// foo?.bar -> foo.bar
1292+
const normalizedPath = path.replace(/\?$/, '');
12901293
if (child.isSatisfiedRecursively) {
12911294
if (child.hasRequiredNodesBelow) {
12921295
// Remember this dep actually satisfied something.
1293-
satisfyingPaths.add(path);
1296+
satisfyingPaths.add(normalizedPath);
12941297
}
12951298
// It doesn't matter if there's something deeper.
12961299
// It would be transitively satisfied since we assume immutability.
@@ -1299,7 +1302,7 @@ function collectRecommendations({
12991302
}
13001303
if (child.isRequired) {
13011304
// Remember that no declared deps satisfied this node.
1302-
missingPaths.add(path);
1305+
missingPaths.add(normalizedPath);
13031306
// If we got here, nothing in its subtree was satisfied.
13041307
// No need to search further.
13051308
return;

0 commit comments

Comments
 (0)