[compiler] Don't include useEffectEvent values in autodeps (#33450)
Summary: useEffectEvent values are not meant to be added to the dep array
Jordan Brown committed
Jun 9, 2025 at 09:26 UTC
4df098c4c2c51a033592ebc84abc47cc49a6bfb2
6 files changed
+109
-1
compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts
+23
@@ -17,6 +17,7 @@ import {
17
BuiltInSetId,
18
BuiltInUseActionStateId,
19
BuiltInUseContextHookId,
20
+ BuiltInUseEffectEventId,
21
BuiltInUseEffectHookId,
22
BuiltInUseInsertionEffectHookId,
23
BuiltInUseLayoutEffectHookId,
@@ -27,6 +28,7 @@ import {
28
BuiltInUseTransitionId,
29
BuiltInWeakMapId,
30
BuiltInWeakSetId,
31
+ BuiltinEffectEventId,
32
ReanimatedSharedValueId,
33
ShapeRegistry,
34
addFunction,
@@ -722,6 +724,27 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
724
BuiltInFireId,
725
),
726
],
727
+ [
728
+ 'useEffectEvent',
729
+ addHook(
730
+ DEFAULT_SHAPES,
731
+ {
732
+ positionalParams: [],
733
+ restParam: Effect.Freeze,
734
+ returnType: {
735
+ kind: 'Function',
736
+ return: {kind: 'Poly'},
737
+ shapeId: BuiltinEffectEventId,
738
+ isConstructor: false,
739
+ },
740
+ calleeEffect: Effect.Read,
741
+ hookKind: 'useEffectEvent',
742
+ // Frozen because it should not mutate any locally-bound values
743
+ returnValueKind: ValueKind.Frozen,
744
+ },
745
+ BuiltInUseEffectEventId,
746
+ ),
747
+ ],
748
];
749
750
TYPED_GLOBALS.push(
compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
+7
@@ -1785,6 +1785,13 @@ export function isFireFunctionType(id: Identifier): boolean {
1785
);
1786
}
1787
1788
+export function isEffectEventFunctionType(id: Identifier): boolean {
1789
+ return (
1790
+ id.type.kind === 'Function' &&
1791
+ id.type.shapeId === 'BuiltInEffectEventFunction'
1792
+ );
1793
+}
1794
+
1795
export function isStableType(id: Identifier): boolean {
1796
return (
1797
isSetStateType(id) ||
compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts
+16
@@ -131,6 +131,7 @@ export type HookKind =
131
| 'useCallback'
132
| 'useTransition'
133
| 'useImperativeHandle'
134
+ | 'useEffectEvent'
135
| 'Custom';
136
137
/*
@@ -226,6 +227,8 @@ export const BuiltInUseTransitionId = 'BuiltInUseTransition';
227
export const BuiltInStartTransitionId = 'BuiltInStartTransition';
228
export const BuiltInFireId = 'BuiltInFire';
229
export const BuiltInFireFunctionId = 'BuiltInFireFunction';
230
+export const BuiltInUseEffectEventId = 'BuiltInUseEffectEvent';
231
+export const BuiltinEffectEventId = 'BuiltInEffectEventFunction';
232
233
// See getReanimatedModuleType() in Globals.ts — this is part of supporting Reanimated's ref-like types
234
export const ReanimatedSharedValueId = 'ReanimatedSharedValueId';
@@ -948,6 +951,19 @@ addObject(BUILTIN_SHAPES, BuiltInRefValueId, [
951
['*', {kind: 'Object', shapeId: BuiltInRefValueId}],
952
]);
953
954
+addFunction(
955
+ BUILTIN_SHAPES,
956
+ [],
957
+ {
958
+ positionalParams: [],
959
+ restParam: Effect.ConditionallyMutate,
960
+ returnType: {kind: 'Poly'},
961
+ calleeEffect: Effect.ConditionallyMutate,
962
+ returnValueKind: ValueKind.Mutable,
963
+ },
964
+ BuiltinEffectEventId,
965
+);
966
+
967
/**
968
* MixedReadOnly =
969
* | primitive
compiler/packages/babel-plugin-react-compiler/src/Inference/InferEffectDependencies.ts
+3
-1
@@ -31,6 +31,7 @@ import {
31
HIR,
32
BasicBlock,
33
BlockId,
34
+ isEffectEventFunctionType,
35
} from '../HIR';
36
import {collectHoistablePropertyLoadsInInnerFn} from '../HIR/CollectHoistablePropertyLoads';
37
import {collectOptionalChainSidemap} from '../HIR/CollectOptionalChainDependencies';
@@ -209,7 +210,8 @@ export function inferEffectDependencies(fn: HIRFunction): void {
210
((isUseRefType(maybeDep.identifier) ||
211
isSetStateType(maybeDep.identifier)) &&
212
!reactiveIds.has(maybeDep.identifier.id)) ||
212
- isFireFunctionType(maybeDep.identifier)
213
+ isFireFunctionType(maybeDep.identifier) ||
214
+ isEffectEventFunctionType(maybeDep.identifier)
215
) {
216
// exclude non-reactive hook results, which will never be in a memo block
217
continue;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-effect-event.expect.md
new
+49
@@ -0,0 +1,49 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @inferEffectDependencies
6
+import {useEffect, useEffectEvent} from 'react';
7
+import {print} from 'shared-runtime';
8
+
9
+/**
10
+ * We do not include effect events in dep arrays.
11
+ */
12
+function NonReactiveEffectEvent() {
13
+ const fn = useEffectEvent(() => print('hello world'));
14
+ useEffect(() => fn());
15
+}
16
+
17
+```
18
+
19
+## Code
20
+
21
+```javascript
22
+import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
23
+import { useEffect, useEffectEvent } from "react";
24
+import { print } from "shared-runtime";
25
+
26
+/**
27
+ * We do not include effect events in dep arrays.
28
+ */
29
+function NonReactiveEffectEvent() {
30
+ const $ = _c(2);
31
+ const fn = useEffectEvent(_temp);
32
+ let t0;
33
+ if ($[0] !== fn) {
34
+ t0 = () => fn();
35
+ $[0] = fn;
36
+ $[1] = t0;
37
+ } else {
38
+ t0 = $[1];
39
+ }
40
+ useEffect(t0, []);
41
+}
42
+function _temp() {
43
+ return print("hello world");
44
+}
45
+
46
+```
47
+
48
+### Eval output
49
+(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/nonreactive-effect-event.js
new
+11
@@ -0,0 +1,11 @@
1
+// @inferEffectDependencies
2
+import {useEffect, useEffectEvent} from 'react';
3
+import {print} from 'shared-runtime';
4
+
5
+/**
6
+ * We do not include effect events in dep arrays.
7
+ */
8
+function NonReactiveEffectEvent() {
9
+ const fn = useEffectEvent(() => print('hello world'));
10
+ useEffect(() => fn());
11
+}