From 7abe1acc96160733bbd7300b977f9cf7602a822b Mon Sep 17 00:00:00 2001 From: Afzal Sayed <14029371+afzalsayed96@users.noreply.github.com> Date: Tue, 12 Apr 2022 00:15:15 +0530 Subject: [PATCH 1/4] Fix exhaustive deps for unstable vars --- .../ESLintRuleExhaustiveDeps-test.js | 58 +++++++++++++++++++ .../src/ExhaustiveDeps.js | 9 ++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js index 4b6b3370e8b2..5c3c64c173fa 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js @@ -1095,6 +1095,22 @@ const tests = { } `, }, + { + code: normalizeIndent` + function Counter(unstableProp) { + let [count, setCount] = useState(0); + setCount = unstableProp + useEffect(() => { + let id = setInterval(() => { + setCount(c => c + 1); + }, 1000); + return () => clearInterval(id); + }, [setCount]); + + return

{count}

; + } + `, + }, { code: normalizeIndent` function Counter() { @@ -1581,6 +1597,48 @@ const tests = { }, ], }, + { + code: normalizeIndent` + function Counter(unstableProp) { + let [count, setCount] = useState(0); + setCount = unstableProp + useEffect(() => { + let id = setInterval(() => { + setCount(c => c + 1); + }, 1000); + return () => clearInterval(id); + }, []); + + return

{count}

; + } + `, + errors: [ + { + message: + "React Hook useEffect has a missing dependency: 'setCount'. " + + 'Either include it or remove the dependency array.', + suggestions: [ + { + desc: 'Update the dependencies array to be: [setCount]', + output: normalizeIndent` + function Counter(unstableProp) { + let [count, setCount] = useState(0); + setCount = unstableProp + useEffect(() => { + let id = setInterval(() => { + setCount(c => c + 1); + }, 1000); + return () => clearInterval(id); + }, [setCount]); + + return

{count}

; + } + `, + }, + ], + }, + ], + }, { // Note: we *could* detect it's a primitive and never assigned // even though it's not a constant -- but we currently don't. diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index 21d48141717c..87767648f18a 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -234,6 +234,9 @@ export default { if (id.elements[1] === resolved.identifiers[0]) { if (name === 'useState') { const references = resolved.references; + // console.log(references); + if (references.filter(ref => ref.isWrite()).length > 1) + return false; for (let i = 0; i < references.length; i++) { setStateCallSites.set( references[i].identifier, @@ -321,7 +324,7 @@ export default { pureScopes.has(ref.resolved.scope) && // Stable values are fine though, // although we won't check functions deeper. - !memoizedIsStablecKnownHookValue(ref.resolved) + !memoizedIsStableKnownHookValue(ref.resolved) ) { return false; } @@ -332,7 +335,7 @@ export default { } // Remember such values. Avoid re-running extra checks on them. - const memoizedIsStablecKnownHookValue = memoizeWithWeakMap( + const memoizedIsStableKnownHookValue = memoizeWithWeakMap( isStableKnownHookValue, stableKnownValueCache, ); @@ -435,7 +438,7 @@ export default { if (!dependencies.has(dependency)) { const resolved = reference.resolved; const isStable = - memoizedIsStablecKnownHookValue(resolved) || + memoizedIsStableKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved); dependencies.set(dependency, { isStable, From bcf5beaf5f19f7fa2945be30fbc0d7a1dfa73185 Mon Sep 17 00:00:00 2001 From: Afzal Sayed <14029371+afzalsayed96@users.noreply.github.com> Date: Tue, 12 Apr 2022 00:34:32 +0530 Subject: [PATCH 2/4] Fix formatting --- packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index 87767648f18a..ab5f84404684 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -234,9 +234,9 @@ export default { if (id.elements[1] === resolved.identifiers[0]) { if (name === 'useState') { const references = resolved.references; - // console.log(references); - if (references.filter(ref => ref.isWrite()).length > 1) + if (references.filter(ref => ref.isWrite()).length > 1) { return false; + } for (let i = 0; i < references.length; i++) { setStateCallSites.set( references[i].identifier, From ef66085f07402b976feae9b7586cd1bcbe9bd2b2 Mon Sep 17 00:00:00 2001 From: Afzal Sayed <14029371+afzalsayed96@users.noreply.github.com> Date: Tue, 12 Apr 2022 01:00:57 +0530 Subject: [PATCH 3/4] Optimise iterations --- .../eslint-plugin-react-hooks/src/ExhaustiveDeps.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index ab5f84404684..cf8a6d8af014 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -234,10 +234,14 @@ export default { if (id.elements[1] === resolved.identifiers[0]) { if (name === 'useState') { const references = resolved.references; - if (references.filter(ref => ref.isWrite()).length > 1) { - return false; - } + let writeCount = 0 for (let i = 0; i < references.length; i++) { + if (references[i].isWrite()) { + writeCount++ + } + if (writeCount > 1) { + return false; + } setStateCallSites.set( references[i].identifier, id.elements[0], From 9152b75a8c8cd89a3890bb5e483cd6eb312b0617 Mon Sep 17 00:00:00 2001 From: Afzal Sayed <14029371+afzalsayed96@users.noreply.github.com> Date: Tue, 12 Apr 2022 01:36:45 +0530 Subject: [PATCH 4/4] Fix linting --- packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index cf8a6d8af014..26d9688ac17c 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -234,10 +234,10 @@ export default { if (id.elements[1] === resolved.identifiers[0]) { if (name === 'useState') { const references = resolved.references; - let writeCount = 0 + let writeCount = 0; for (let i = 0; i < references.length; i++) { if (references[i].isWrite()) { - writeCount++ + writeCount++; } if (writeCount > 1) { return false;