Skip to content

Commit 84a0a17

Browse files
authored
Rename experimental useEvent to useEffectEvent (#25881)
We originally had grand plans for using this Event concept for more but now it's only meant to be used in combination with effects. It's an Event in the FRP terms, that is triggered from an Effect. Technically it can also be from another function that itself is triggered from an existing side-effect but that's kind of an advanced case. The canonical case is an effect that triggers an event: ```js const onHappened = useEffectEvent(() => ...); useEffect(() => { onHappened(); }, []); ```
1 parent 4dda96a commit 84a0a17

26 files changed

+176
-171
lines changed

packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7636,7 +7636,7 @@ if (__EXPERIMENTAL__) {
76367636
{
76377637
code: normalizeIndent`
76387638
function MyComponent({ theme }) {
7639-
const onStuff = useEvent(() => {
7639+
const onStuff = useEffectEvent(() => {
76407640
showNotification(theme);
76417641
});
76427642
useEffect(() => {
@@ -7652,7 +7652,7 @@ if (__EXPERIMENTAL__) {
76527652
{
76537653
code: normalizeIndent`
76547654
function MyComponent({ theme }) {
7655-
const onStuff = useEvent(() => {
7655+
const onStuff = useEffectEvent(() => {
76567656
showNotification(theme);
76577657
});
76587658
useEffect(() => {
@@ -7663,14 +7663,14 @@ if (__EXPERIMENTAL__) {
76637663
errors: [
76647664
{
76657665
message:
7666-
'Functions returned from `useEvent` must not be included in the dependency array. ' +
7666+
'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
76677667
'Remove `onStuff` from the list.',
76687668
suggestions: [
76697669
{
76707670
desc: 'Remove the dependency `onStuff`',
76717671
output: normalizeIndent`
76727672
function MyComponent({ theme }) {
7673-
const onStuff = useEvent(() => {
7673+
const onStuff = useEffectEvent(() => {
76747674
showNotification(theme);
76757675
});
76767676
useEffect(() => {

packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,9 +1050,9 @@ if (__EXPERIMENTAL__) {
10501050
...tests.valid,
10511051
{
10521052
code: normalizeIndent`
1053-
// Valid because functions created with useEvent can be called in a useEffect.
1053+
// Valid because functions created with useEffectEvent can be called in a useEffect.
10541054
function MyComponent({ theme }) {
1055-
const onClick = useEvent(() => {
1055+
const onClick = useEffectEvent(() => {
10561056
showNotification(theme);
10571057
});
10581058
useEffect(() => {
@@ -1063,9 +1063,9 @@ if (__EXPERIMENTAL__) {
10631063
},
10641064
{
10651065
code: normalizeIndent`
1066-
// Valid because functions created with useEvent can be called in closures.
1066+
// Valid because functions created with useEffectEvent can be called in closures.
10671067
function MyComponent({ theme }) {
1068-
const onClick = useEvent(() => {
1068+
const onClick = useEffectEvent(() => {
10691069
showNotification(theme);
10701070
});
10711071
return <Child onClick={() => onClick()}></Child>;
@@ -1074,9 +1074,9 @@ if (__EXPERIMENTAL__) {
10741074
},
10751075
{
10761076
code: normalizeIndent`
1077-
// Valid because functions created with useEvent can be called in closures.
1077+
// Valid because functions created with useEffectEvent can be called in closures.
10781078
function MyComponent({ theme }) {
1079-
const onClick = useEvent(() => {
1079+
const onClick = useEffectEvent(() => {
10801080
showNotification(theme);
10811081
});
10821082
const onClick2 = () => { onClick() };
@@ -1090,13 +1090,13 @@ if (__EXPERIMENTAL__) {
10901090
},
10911091
{
10921092
code: normalizeIndent`
1093-
// Valid because functions created with useEvent can be passed by reference in useEffect
1094-
// and useEvent.
1093+
// Valid because functions created with useEffectEvent can be passed by reference in useEffect
1094+
// and useEffectEvent.
10951095
function MyComponent({ theme }) {
1096-
const onClick = useEvent(() => {
1096+
const onClick = useEffectEvent(() => {
10971097
showNotification(theme);
10981098
});
1099-
const onClick2 = useEvent(() => {
1099+
const onClick2 = useEffectEvent(() => {
11001100
debounce(onClick);
11011101
});
11021102
useEffect(() => {
@@ -1110,7 +1110,7 @@ if (__EXPERIMENTAL__) {
11101110
{
11111111
code: normalizeIndent`
11121112
const MyComponent = ({theme}) => {
1113-
const onClick = useEvent(() => {
1113+
const onClick = useEffectEvent(() => {
11141114
showNotification(theme);
11151115
});
11161116
return <Child onClick={() => onClick()}></Child>;
@@ -1121,10 +1121,10 @@ if (__EXPERIMENTAL__) {
11211121
code: normalizeIndent`
11221122
function MyComponent({ theme }) {
11231123
const notificationService = useNotifications();
1124-
const showNotification = useEvent((text) => {
1124+
const showNotification = useEffectEvent((text) => {
11251125
notificationService.notify(theme, text);
11261126
});
1127-
const onClick = useEvent((text) => {
1127+
const onClick = useEffectEvent((text) => {
11281128
showNotification(text);
11291129
});
11301130
return <Child onClick={(text) => onClick(text)} />
@@ -1137,7 +1137,7 @@ if (__EXPERIMENTAL__) {
11371137
useEffect(() => {
11381138
onClick();
11391139
});
1140-
const onClick = useEvent(() => {
1140+
const onClick = useEffectEvent(() => {
11411141
showNotification(theme);
11421142
});
11431143
}
@@ -1188,64 +1188,64 @@ if (__EXPERIMENTAL__) {
11881188
{
11891189
code: normalizeIndent`
11901190
function MyComponent({ theme }) {
1191-
const onClick = useEvent(() => {
1191+
const onClick = useEffectEvent(() => {
11921192
showNotification(theme);
11931193
});
11941194
return <Child onClick={onClick}></Child>;
11951195
}
11961196
`,
1197-
errors: [useEventError('onClick')],
1197+
errors: [useEffectEventError('onClick')],
11981198
},
11991199
{
12001200
code: normalizeIndent`
12011201
// This should error even though it shares an identifier name with the below
12021202
function MyComponent({theme}) {
1203-
const onClick = useEvent(() => {
1203+
const onClick = useEffectEvent(() => {
12041204
showNotification(theme)
12051205
});
12061206
return <Child onClick={onClick} />
12071207
}
12081208
1209-
// The useEvent function shares an identifier name with the above
1209+
// The useEffectEvent function shares an identifier name with the above
12101210
function MyOtherComponent({theme}) {
1211-
const onClick = useEvent(() => {
1211+
const onClick = useEffectEvent(() => {
12121212
showNotification(theme)
12131213
});
12141214
return <Child onClick={() => onClick()} />
12151215
}
12161216
`,
1217-
errors: [{...useEventError('onClick'), line: 7}],
1217+
errors: [{...useEffectEventError('onClick'), line: 7}],
12181218
},
12191219
{
12201220
code: normalizeIndent`
12211221
const MyComponent = ({ theme }) => {
1222-
const onClick = useEvent(() => {
1222+
const onClick = useEffectEvent(() => {
12231223
showNotification(theme);
12241224
});
12251225
return <Child onClick={onClick}></Child>;
12261226
}
12271227
`,
1228-
errors: [useEventError('onClick')],
1228+
errors: [useEffectEventError('onClick')],
12291229
},
12301230
{
12311231
code: normalizeIndent`
12321232
// Invalid because onClick is being aliased to foo but not invoked
12331233
function MyComponent({ theme }) {
1234-
const onClick = useEvent(() => {
1234+
const onClick = useEffectEvent(() => {
12351235
showNotification(theme);
12361236
});
12371237
let foo = onClick;
12381238
return <Bar onClick={foo} />
12391239
}
12401240
`,
1241-
errors: [{...useEventError('onClick'), line: 7}],
1241+
errors: [{...useEffectEventError('onClick'), line: 7}],
12421242
},
12431243
{
12441244
code: normalizeIndent`
12451245
// Should error because it's being passed down to JSX, although it's been referenced once
12461246
// in an effect
12471247
function MyComponent({ theme }) {
1248-
const onClick = useEvent(() => {
1248+
const onClick = useEffectEvent(() => {
12491249
showNotification(them);
12501250
});
12511251
useEffect(() => {
@@ -1254,7 +1254,7 @@ if (__EXPERIMENTAL__) {
12541254
return <Child onClick={onClick} />
12551255
}
12561256
`,
1257-
errors: [useEventError('onClick')],
1257+
errors: [useEffectEventError('onClick')],
12581258
},
12591259
{
12601260
code: normalizeIndent`
@@ -1360,10 +1360,10 @@ function classError(hook) {
13601360
};
13611361
}
13621362

1363-
function useEventError(fn) {
1363+
function useEffectEventError(fn) {
13641364
return {
13651365
message:
1366-
`\`${fn}\` is a function created with React Hook "useEvent", and can only be called from ` +
1366+
`\`${fn}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
13671367
'the same component. They cannot be assigned to variables or passed down.',
13681368
};
13691369
}

packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default {
7474
const stateVariables = new WeakSet();
7575
const stableKnownValueCache = new WeakMap();
7676
const functionWithoutCapturedValueCache = new WeakMap();
77-
const useEventVariables = new WeakSet();
77+
const useEffectEventVariables = new WeakSet();
7878
function memoizeWithWeakMap(fn, map) {
7979
return function(arg) {
8080
if (map.has(arg)) {
@@ -158,7 +158,7 @@ export default {
158158
// ^^^ true for this reference
159159
// const ref = useRef()
160160
// ^^^ true for this reference
161-
// const onStuff = useEvent(() => {})
161+
// const onStuff = useEffectEvent(() => {})
162162
// ^^^ true for this reference
163163
// False for everything else.
164164
function isStableKnownHookValue(resolved) {
@@ -226,13 +226,16 @@ export default {
226226
if (name === 'useRef' && id.type === 'Identifier') {
227227
// useRef() return value is stable.
228228
return true;
229-
} else if (isUseEventIdentifier(callee) && id.type === 'Identifier') {
229+
} else if (
230+
isUseEffectEventIdentifier(callee) &&
231+
id.type === 'Identifier'
232+
) {
230233
for (const ref of resolved.references) {
231234
if (ref !== id) {
232-
useEventVariables.add(ref.identifier);
235+
useEffectEventVariables.add(ref.identifier);
233236
}
234237
}
235-
// useEvent() return value is always unstable.
238+
// useEffectEvent() return value is always unstable.
236239
return true;
237240
} else if (name === 'useState' || name === 'useReducer') {
238241
// Only consider second value in initializing tuple stable.
@@ -645,11 +648,11 @@ export default {
645648
});
646649
return;
647650
}
648-
if (useEventVariables.has(declaredDependencyNode)) {
651+
if (useEffectEventVariables.has(declaredDependencyNode)) {
649652
reportProblem({
650653
node: declaredDependencyNode,
651654
message:
652-
'Functions returned from `useEvent` must not be included in the dependency array. ' +
655+
'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
653656
`Remove \`${context.getSource(
654657
declaredDependencyNode,
655658
)}\` from the list.`,
@@ -1851,9 +1854,9 @@ function isAncestorNodeOf(a, b) {
18511854
return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
18521855
}
18531856

1854-
function isUseEventIdentifier(node) {
1857+
function isUseEffectEventIdentifier(node) {
18551858
if (__EXPERIMENTAL__) {
1856-
return node.type === 'Identifier' && node.name === 'useEvent';
1859+
return node.type === 'Identifier' && node.name === 'useEffectEvent';
18571860
}
18581861
return false;
18591862
}

packages/eslint-plugin-react-hooks/src/RulesOfHooks.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ function isInsideComponentOrHook(node) {
103103
return false;
104104
}
105105

106-
function isUseEventIdentifier(node) {
106+
function isUseEffectEventIdentifier(node) {
107107
if (__EXPERIMENTAL__) {
108-
return node.type === 'Identifier' && node.name === 'useEvent';
108+
return node.type === 'Identifier' && node.name === 'useEffectEvent';
109109
}
110110
return false;
111111
}
@@ -130,24 +130,24 @@ export default {
130130
let lastEffect = null;
131131
const codePathReactHooksMapStack = [];
132132
const codePathSegmentStack = [];
133-
const useEventFunctions = new WeakSet();
133+
const useEffectEventFunctions = new WeakSet();
134134

135-
// For a given scope, iterate through the references and add all useEvent definitions. We can
136-
// do this in non-Program nodes because we can rely on the assumption that useEvent functions
135+
// For a given scope, iterate through the references and add all useEffectEvent definitions. We can
136+
// do this in non-Program nodes because we can rely on the assumption that useEffectEvent functions
137137
// can only be declared within a component or hook at its top level.
138-
function recordAllUseEventFunctions(scope) {
138+
function recordAllUseEffectEventFunctions(scope) {
139139
for (const reference of scope.references) {
140140
const parent = reference.identifier.parent;
141141
if (
142142
parent.type === 'VariableDeclarator' &&
143143
parent.init &&
144144
parent.init.type === 'CallExpression' &&
145145
parent.init.callee &&
146-
isUseEventIdentifier(parent.init.callee)
146+
isUseEffectEventIdentifier(parent.init.callee)
147147
) {
148148
for (const ref of reference.resolved.references) {
149149
if (ref !== reference) {
150-
useEventFunctions.add(ref.identifier);
150+
useEffectEventFunctions.add(ref.identifier);
151151
}
152152
}
153153
}
@@ -571,12 +571,12 @@ export default {
571571
reactHooks.push(node.callee);
572572
}
573573

574-
// useEvent: useEvent functions can be passed by reference within useEffect as well as in
575-
// another useEvent
574+
// useEffectEvent: useEffectEvent functions can be passed by reference within useEffect as well as in
575+
// another useEffectEvent
576576
if (
577577
node.callee.type === 'Identifier' &&
578578
(node.callee.name === 'useEffect' ||
579-
isUseEventIdentifier(node.callee)) &&
579+
isUseEffectEventIdentifier(node.callee)) &&
580580
node.arguments.length > 0
581581
) {
582582
// Denote that we have traversed into a useEffect call, and stash the CallExpr for
@@ -586,19 +586,19 @@ export default {
586586
},
587587

588588
Identifier(node) {
589-
// This identifier resolves to a useEvent function, but isn't being referenced in an
589+
// This identifier resolves to a useEffectEvent function, but isn't being referenced in an
590590
// effect or another event function. It isn't being called either.
591591
if (
592592
lastEffect == null &&
593-
useEventFunctions.has(node) &&
593+
useEffectEventFunctions.has(node) &&
594594
node.parent.type !== 'CallExpression'
595595
) {
596596
context.report({
597597
node,
598598
message:
599599
`\`${context.getSource(
600600
node,
601-
)}\` is a function created with React Hook "useEvent", and can only be called from ` +
601+
)}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
602602
'the same component. They cannot be assigned to variables or passed down.',
603603
});
604604
}
@@ -611,16 +611,16 @@ export default {
611611
},
612612

613613
FunctionDeclaration(node) {
614-
// function MyComponent() { const onClick = useEvent(...) }
614+
// function MyComponent() { const onClick = useEffectEvent(...) }
615615
if (isInsideComponentOrHook(node)) {
616-
recordAllUseEventFunctions(context.getScope());
616+
recordAllUseEffectEventFunctions(context.getScope());
617617
}
618618
},
619619

620620
ArrowFunctionExpression(node) {
621-
// const MyComponent = () => { const onClick = useEvent(...) }
621+
// const MyComponent = () => { const onClick = useEffectEvent(...) }
622622
if (isInsideComponentOrHook(node)) {
623-
recordAllUseEventFunctions(context.getScope());
623+
recordAllUseEffectEventFunctions(context.getScope());
624624
}
625625
},
626626
};

0 commit comments

Comments
 (0)