From 897976600c2277e70601d771992f26ba5c1a14f0 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 19 Dec 2019 17:04:17 +0000 Subject: [PATCH] [ESLint] Allow partial matches for custom Effect Hooks (#17663) --- .../ESLintRuleExhaustiveDeps-test.js | 31 +++++++++++++++++++ .../src/ExhaustiveDeps.js | 2 +- 2 files changed, 32 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 a9648151a2d7..aa25e13ba512 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js @@ -3154,6 +3154,37 @@ const tests = { `and use that variable in the cleanup function.`, ], }, + { + code: ` + function MyComponent() { + const myRef = useRef(); + useLayoutEffect_SAFE_FOR_SSR(() => { + const handleMove = () => {}; + myRef.current.addEventListener('mousemove', handleMove); + return () => myRef.current.removeEventListener('mousemove', handleMove); + }); + return
; + } + `, + output: ` + function MyComponent() { + const myRef = useRef(); + useLayoutEffect_SAFE_FOR_SSR(() => { + const handleMove = () => {}; + myRef.current.addEventListener('mousemove', handleMove); + return () => myRef.current.removeEventListener('mousemove', handleMove); + }); + return
; + } + `, + errors: [ + `The ref value 'myRef.current' will likely have changed by the time ` + + `this effect cleanup function runs. If this ref points to a node ` + + `rendered by React, copy 'myRef.current' to a variable inside the effect, ` + + `and use that variable in the cleanup function.`, + ], + options: [{additionalHooks: 'useLayoutEffect_SAFE_FOR_SSR'}], + }, { // Autofix ignores constant primitives (leaving the ones that are there). code: ` diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js index 37e315b429b3..6e96ecddea54 100644 --- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js +++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js @@ -81,7 +81,7 @@ export default { // Get the reactive hook node. const reactiveHook = node.parent.callee; const reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name; - const isEffect = reactiveHookName.endsWith('Effect'); + const isEffect = /Effect($|[^a-z])/g.test(reactiveHookName); // Get the declared dependencies for this reactive hook. If there is no // second argument then the reactive callback will re-run on every render.