diff --git a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs index b110ccf742e19..4cae2f2ca210e 100644 --- a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs +++ b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs @@ -1226,6 +1226,16 @@ impl<'a, 'b> ExhaustiveDepsVisitor<'a, 'b> { return None; }; + // Only apply destructuring logic when the identifier is directly the RHS of + // the destructuring assignment, not when it's nested inside another expression + // like a function call. + // For example: + // - `const { headers } = props` -> props.headers is the dependency + // - `const { headers } = fn(booleanValue)` -> booleanValue is the dependency, not booleanValue.headers + if self.stack.contains(&AstType::CallExpression) { + return None; + } + if obj.rest.is_some() { return Some(true); } @@ -4297,6 +4307,12 @@ fn test() { "const x = {}; function Comp() { useEffect(() => {}, [x]) }", "const x = {}; function Comp() { useEffect(() => {}, []) }", ), + // Issue #17159: fixer should suggest `booleanValue`, not `booleanValue.headers` + // The destructuring pattern `{ headers }` should not affect the dependency name + ( + "function Comp() { const booleanValue = useMemo(() => true, []); const foo = useMemo(() => { const { headers } = fn(booleanValue); return headers; }, []); }", + "function Comp() { const booleanValue = useMemo(() => true, []); const foo = useMemo(() => { const { headers } = fn(booleanValue); return headers; }, [booleanValue]); }", + ), ]; Tester::new( diff --git a/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap b/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap index aa187c580431b..590ec8ab7db1f 100644 --- a/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap +++ b/crates/oxc_linter/src/snapshots/react_exhaustive_deps.snap @@ -700,12 +700,12 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Either include it or remove the dependency array. - ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'foo.bar' + ⚠ eslint-plugin-react-hooks(exhaustive-deps): React Hook useEffect has a missing dependency: 'foo' ╭─[exhaustive_deps.tsx:6:14] 3 │ useEffect(() => { 4 │ const { bar } = foo(); · ─┬─ - · ╰── useEffect uses `foo.bar` here + · ╰── useEffect uses `foo` here 5 │ console.log(bar); 6 │ }, [props.foo.bar]); · ───────────────