From 6d2666bab16ee8c822e848f84a6feb0d42a6d78c Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 7 Mar 2019 00:39:39 +0000 Subject: [PATCH] Fix ESLint rule crash (#15044) --- .../ESLintRuleExhaustiveDeps-test.js | 37 +++++++++++++++++++ .../src/ExhaustiveDeps.js | 12 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js index 0c3bd909ca253..a1c7932368507 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js @@ -838,6 +838,17 @@ const tests = { } `, }, + { + // Regression test for a crash + code: ` + function Podcasts() { + useEffect(() => { + setPodcasts([]); + }, []); + let [podcasts, setPodcasts] = useState(null); + } + `, + }, ], invalid: [ { @@ -3812,6 +3823,32 @@ const tests = { 'Either include it or remove the dependency array.', ], }, + { + // Regression test for a crash + code: ` + function Podcasts() { + useEffect(() => { + alert(podcasts); + }, []); + let [podcasts, setPodcasts] = useState(null); + } + `, + // Note: this autofix is shady because + // the variable is used before declaration. + // TODO: Maybe we can catch those fixes and not autofix. + output: ` + function Podcasts() { + useEffect(() => { + alert(podcasts); + }, [podcasts]); + let [podcasts, setPodcasts] = useState(null); + } + `, + errors: [ + `React Hook useEffect has a missing dependency: 'podcasts'. ` + + `Either include it or remove the dependency array.`, + ], + }, ], }; diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index 252f29a703d0d..2f8b18c0069a7 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -164,7 +164,17 @@ export default { } // Detect primitive constants // const foo = 42 - const declaration = def.node.parent; + let declaration = def.node.parent; + if (declaration == null) { + // This might happen if variable is declared after the callback. + // In that case ESLint won't set up .parent refs. + // So we'll set them up manually. + fastFindReferenceWithParent(componentScope.block, def.node.id); + declaration = def.node.parent; + if (declaration == null) { + return false; + } + } if ( declaration.kind === 'const' && init.type === 'Literal' &&