diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
index 055474ea321e0..848433ae97f06 100644
--- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
+++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js
@@ -7675,62 +7675,6 @@ const tests = {
],
};
-if (__EXPERIMENTAL__) {
- tests.valid = [
- ...tests.valid,
- {
- code: normalizeIndent`
- function MyComponent({ theme }) {
- const onStuff = useEffectEvent(() => {
- showNotification(theme);
- });
- useEffect(() => {
- onStuff();
- }, []);
- }
- `,
- },
- ];
-
- tests.invalid = [
- ...tests.invalid,
- {
- code: normalizeIndent`
- function MyComponent({ theme }) {
- const onStuff = useEffectEvent(() => {
- showNotification(theme);
- });
- useEffect(() => {
- onStuff();
- }, [onStuff]);
- }
- `,
- errors: [
- {
- message:
- 'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
- 'Remove `onStuff` from the list.',
- suggestions: [
- {
- desc: 'Remove the dependency `onStuff`',
- output: normalizeIndent`
- function MyComponent({ theme }) {
- const onStuff = useEffectEvent(() => {
- showNotification(theme);
- });
- useEffect(() => {
- onStuff();
- }, []);
- }
- `,
- },
- ],
- },
- ],
- },
- ];
-}
-
// Tests that are only valid/invalid across parsers supporting Flow
const testsFlow = {
valid: [
diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js
index b8ec97678aa16..1789c6675f208 100644
--- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js
+++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js
@@ -1286,181 +1286,6 @@ const tests = {
],
};
-if (__EXPERIMENTAL__) {
- tests.valid = [
- ...tests.valid,
- {
- code: normalizeIndent`
- // Valid because functions created with useEffectEvent can be called in a useEffect.
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- useEffect(() => {
- onClick();
- });
- }
- `,
- },
- {
- code: normalizeIndent`
- // Valid because functions created with useEffectEvent can be called in closures.
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- return onClick()}>;
- }
- `,
- },
- {
- code: normalizeIndent`
- // Valid because functions created with useEffectEvent can be called in closures.
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- const onClick2 = () => { onClick() };
- const onClick3 = useCallback(() => onClick(), []);
- return <>
-
-
- >;
- }
- `,
- },
- {
- code: normalizeIndent`
- // Valid because functions created with useEffectEvent can be passed by reference in useEffect
- // and useEffectEvent.
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- const onClick2 = useEffectEvent(() => {
- debounce(onClick);
- });
- useEffect(() => {
- let id = setInterval(onClick, 100);
- return () => clearInterval(onClick);
- }, []);
- return onClick2()} />
- }
- `,
- },
- {
- code: normalizeIndent`
- const MyComponent = ({theme}) => {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- return onClick()}>;
- };
- `,
- },
- {
- code: normalizeIndent`
- function MyComponent({ theme }) {
- const notificationService = useNotifications();
- const showNotification = useEffectEvent((text) => {
- notificationService.notify(theme, text);
- });
- const onClick = useEffectEvent((text) => {
- showNotification(text);
- });
- return onClick(text)} />
- }
- `,
- },
- {
- code: normalizeIndent`
- function MyComponent({ theme }) {
- useEffect(() => {
- onClick();
- });
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- }
- `,
- },
- ];
- tests.invalid = [
- ...tests.invalid,
- {
- code: normalizeIndent`
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- return ;
- }
- `,
- errors: [useEffectEventError('onClick')],
- },
- {
- code: normalizeIndent`
- // This should error even though it shares an identifier name with the below
- function MyComponent({theme}) {
- const onClick = useEffectEvent(() => {
- showNotification(theme)
- });
- return
- }
-
- // The useEffectEvent function shares an identifier name with the above
- function MyOtherComponent({theme}) {
- const onClick = useEffectEvent(() => {
- showNotification(theme)
- });
- return onClick()} />
- }
- `,
- errors: [{...useEffectEventError('onClick'), line: 7}],
- },
- {
- code: normalizeIndent`
- const MyComponent = ({ theme }) => {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- return ;
- }
- `,
- errors: [useEffectEventError('onClick')],
- },
- {
- code: normalizeIndent`
- // Invalid because onClick is being aliased to foo but not invoked
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(theme);
- });
- let foo = onClick;
- return
- }
- `,
- errors: [{...useEffectEventError('onClick'), line: 7}],
- },
- {
- code: normalizeIndent`
- // Should error because it's being passed down to JSX, although it's been referenced once
- // in an effect
- function MyComponent({ theme }) {
- const onClick = useEffectEvent(() => {
- showNotification(them);
- });
- useEffect(() => {
- setTimeout(onClick, 100);
- });
- return
- }
- `,
- errors: [useEffectEventError('onClick')],
- },
- ];
-}
-
function conditionalError(hook, hasPreviousFinalizer = false) {
return {
message:
diff --git a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
index b2818327ff9e4..55e10025bf2c7 100644
--- a/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
+++ b/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js
@@ -97,7 +97,6 @@ export default {
const stateVariables = new WeakSet();
const stableKnownValueCache = new WeakMap();
const functionWithoutCapturedValueCache = new WeakMap();
- const useEffectEventVariables = new WeakSet();
function memoizeWithWeakMap(fn, map) {
return function (arg) {
if (map.has(arg)) {
@@ -183,8 +182,6 @@ export default {
// ^^^ true for this reference
// const ref = useRef()
// ^^^ true for this reference
- // const onStuff = useEffectEvent(() => {})
- // ^^^ true for this reference
// False for everything else.
function isStableKnownHookValue(resolved) {
if (!isArray(resolved.defs)) {
@@ -251,17 +248,6 @@ export default {
if (name === 'useRef' && id.type === 'Identifier') {
// useRef() return value is stable.
return true;
- } else if (
- isUseEffectEventIdentifier(callee) &&
- id.type === 'Identifier'
- ) {
- for (const ref of resolved.references) {
- if (ref !== id) {
- useEffectEventVariables.add(ref.identifier);
- }
- }
- // useEffectEvent() return value is always unstable.
- return true;
} else if (
name === 'useState' ||
name === 'useReducer' ||
@@ -687,26 +673,7 @@ export default {
});
return;
}
- if (useEffectEventVariables.has(declaredDependencyNode)) {
- reportProblem({
- node: declaredDependencyNode,
- message:
- 'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
- `Remove \`${getSource(
- declaredDependencyNode,
- )}\` from the list.`,
- suggest: [
- {
- desc: `Remove the dependency \`${getSource(
- declaredDependencyNode,
- )}\``,
- fix(fixer) {
- return fixer.removeRange(declaredDependencyNode.range);
- },
- },
- ],
- });
- }
+
// Try to normalize the declared dependency. If we can't then an error
// will be thrown. We will catch that error and report an error.
let declaredDependency;
@@ -1910,13 +1877,6 @@ function isAncestorNodeOf(a, b) {
return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
}
-function isUseEffectEventIdentifier(node) {
- if (__EXPERIMENTAL__) {
- return node.type === 'Identifier' && node.name === 'useEffectEvent';
- }
- return false;
-}
-
function getUnknownDependenciesMessage(reactiveHookName) {
return (
`React Hook ${reactiveHookName} received a function whose dependencies ` +
diff --git a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js
index fc340519e233e..67886ca123d5b 100644
--- a/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js
+++ b/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js
@@ -110,13 +110,6 @@ function isInsideDoWhileLoop(node) {
return false;
}
-function isUseEffectEventIdentifier(node) {
- if (__EXPERIMENTAL__) {
- return node.type === 'Identifier' && node.name === 'useEffectEvent';
- }
- return false;
-}
-
function isUseIdentifier(node) {
return isReactFunction(node, 'use');
}
@@ -134,29 +127,6 @@ export default {
let lastEffect = null;
const codePathReactHooksMapStack = [];
const codePathSegmentStack = [];
- const useEffectEventFunctions = new WeakSet();
-
- // For a given scope, iterate through the references and add all useEffectEvent definitions. We can
- // do this in non-Program nodes because we can rely on the assumption that useEffectEvent functions
- // can only be declared within a component or hook at its top level.
- function recordAllUseEffectEventFunctions(scope) {
- for (const reference of scope.references) {
- const parent = reference.identifier.parent;
- if (
- parent.type === 'VariableDeclarator' &&
- parent.init &&
- parent.init.type === 'CallExpression' &&
- parent.init.callee &&
- isUseEffectEventIdentifier(parent.init.callee)
- ) {
- for (const ref of reference.resolved.references) {
- if (ref !== reference) {
- useEffectEventFunctions.add(ref.identifier);
- }
- }
- }
- }
- }
/**
* SourceCode#getText that also works down to ESLint 3.0.0
@@ -169,17 +139,6 @@ export default {
: node => {
return context.sourceCode.getText(node);
};
- /**
- * SourceCode#getScope that also works down to ESLint 3.0.0
- */
- const getScope =
- typeof context.getScope === 'function'
- ? () => {
- return context.getScope();
- }
- : node => {
- return context.sourceCode.getScope(node);
- };
return {
// Maintain code segment path stack as we traverse.
@@ -613,12 +572,9 @@ export default {
reactHooks.push(node.callee);
}
- // useEffectEvent: useEffectEvent functions can be passed by reference within useEffect as well as in
- // another useEffectEvent
if (
node.callee.type === 'Identifier' &&
- (node.callee.name === 'useEffect' ||
- isUseEffectEventIdentifier(node.callee)) &&
+ node.callee.name === 'useEffect' &&
node.arguments.length > 0
) {
// Denote that we have traversed into a useEffect call, and stash the CallExpr for
@@ -627,44 +583,11 @@ export default {
}
},
- Identifier(node) {
- // This identifier resolves to a useEffectEvent function, but isn't being referenced in an
- // effect or another event function. It isn't being called either.
- if (
- lastEffect == null &&
- useEffectEventFunctions.has(node) &&
- node.parent.type !== 'CallExpression'
- ) {
- context.report({
- node,
- message:
- `\`${getSource(
- node,
- )}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
- 'the same component. They cannot be assigned to variables or passed down.',
- });
- }
- },
-
'CallExpression:exit'(node) {
if (node === lastEffect) {
lastEffect = null;
}
},
-
- FunctionDeclaration(node) {
- // function MyComponent() { const onClick = useEffectEvent(...) }
- if (isInsideComponentOrHook(node)) {
- recordAllUseEffectEventFunctions(getScope(node));
- }
- },
-
- ArrowFunctionExpression(node) {
- // const MyComponent = () => { const onClick = useEffectEvent(...) }
- if (isInsideComponentOrHook(node)) {
- recordAllUseEffectEventFunctions(getScope(node));
- }
- },
};
},
};