@samitouri / QOS-React-1 / commits / 4df098c4c2

[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 +}