diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js index 45474333a96db..c8204283986e8 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js @@ -251,6 +251,44 @@ const tests = { } `, }, + // Nullish coalescing and optional chaining + { + code: normalizeIndent` + function MyComponent(props) { + useEffect(() => { + console.log(props.foo?.bar?.baz ?? null); + }, [props.foo]); + } + `, + }, + { + code: normalizeIndent` + function MyComponent(props) { + useEffect(() => { + console.log(props.foo?.bar); + }, [props.foo?.bar]); + } + `, + }, + { + code: normalizeIndent` + function MyComponent(props) { + useEffect(() => { + console.log(props.foo); + console.log(props.foo?.bar); + }, [props.foo]); + } + `, + }, + { + code: normalizeIndent` + function MyComponent(props) { + useEffect(() => { + console.log(props.foo?.toString()); + }, [props.foo]); + } + `, + }, { code: normalizeIndent` function MyComponent() { diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index 72fbf533c2fa9..6fc155d45598a 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -1238,7 +1238,7 @@ function collectRecommendations({ const keys = path.split('.'); let node = rootNode; for (const key of keys) { - let child = node.children.get(key); + let child = getChildByKey(node, key); if (!child) { child = createDepTree(); node.children.set(key, child); @@ -1251,7 +1251,7 @@ function collectRecommendations({ const keys = path.split('.'); let node = rootNode; for (const key of keys) { - const child = node.children.get(key); + const child = getChildByKey(node, key); if (!child) { return; } @@ -1260,6 +1260,21 @@ function collectRecommendations({ } } + /** + * Match key with optional chaining + * key -> key + * key? -> key + * key -> key? + * Otherwise undefined. + */ + function getChildByKey(node, key) { + return ( + node.children.get(key) || + node.children.get(key.split('?')[0]) || + node.children.get(key + '?') + ); + } + // Now we can learn which dependencies are missing or necessary. const missingDependencies = new Set(); const satisfyingDependencies = new Set();