Skip to content

Commit d9ed322

Browse files
committed
WIP: handle useEffectEvent
1 parent 0a93bf8 commit d9ed322

File tree

9 files changed

+183
-6
lines changed

9 files changed

+183
-6
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ export type ManualMemoDependency = {
804804
kind: 'NamedLocal';
805805
value: Place;
806806
constant: boolean;
807+
isEffectEvent: boolean;
807808
}
808809
| {kind: 'Global'; identifierName: string};
809810
path: DependencyPath;

compiler/packages/babel-plugin-react-compiler/src/Inference/DropManualMemoization.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export function collectMaybeMemoDependencies(
9595
kind: 'NamedLocal',
9696
value: {...value.place},
9797
constant: false,
98+
isEffectEvent: false,
9899
},
99100
path: [],
100101
loc: value.place.loc,

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
Identifier,
2626
IdentifierId,
2727
InstructionKind,
28+
isEffectEventFunctionType,
2829
isPrimitiveType,
2930
isStableType,
3031
isSubPath,
@@ -180,6 +181,7 @@ export function validateExhaustiveDependencies(
180181
loc: dep.loc,
181182
reactive: reactive.has(dep.identifier.id),
182183
},
184+
isEffectEvent: isEffectEventFunctionType(dep.identifier),
183185
},
184186
path: dep.path,
185187
loc: dep.loc,
@@ -339,6 +341,23 @@ function validateDependencies(
339341
) {
340342
continue;
341343
}
344+
// if (isEffectEventFunctionType(inferredDependency.identifier)) {
345+
// extra.push({
346+
// ...inferredDependency,
347+
// root: {
348+
// kind: 'NamedLocal',
349+
// isEffectEvent: true,
350+
// constant: false,
351+
// value: {
352+
// kind: 'Identifier',
353+
// identifier: inferredDependency.identifier,
354+
// effect: Effect.Read,
355+
// reactive: false,
356+
// loc: inferredDependency.loc,
357+
// },
358+
// },
359+
// });
360+
// }
342361
missing.push(inferredDependency);
343362
}
344363

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ function validateInferredDep(
269269
reactive: false,
270270
},
271271
constant: false,
272+
isEffectEvent: false,
272273
},
273274
path: [...dep.path],
274275
loc: GeneratedSource,
@@ -383,6 +384,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
383384
kind: 'NamedLocal',
384385
value: storeTarget,
385386
constant: false,
387+
isEffectEvent: false,
386388
},
387389
path: [],
388390
loc: storeTarget.loc,
@@ -414,6 +416,7 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
414416
kind: 'NamedLocal',
415417
value: {...lvalue},
416418
constant: false,
419+
isEffectEvent: false,
417420
},
418421
path: [],
419422
loc: lvalue.loc,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @validateExhaustiveEffectDependencies
2+
import {useEffect, useEffectEvent} from 'react';
3+
4+
function Component({x, y, z}) {
5+
const effectEvent = useEffectEvent(() => {
6+
log(x);
7+
});
8+
9+
// error - do not include effect event in deps
10+
useEffect(() => {
11+
effectEvent();
12+
}, [effectEvent]);
13+
}

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.expect.md

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
## Input
33

44
```javascript
5-
// @validateExhaustiveMemoizationDependencies
5+
// @validateExhaustiveMemoizationDependencies @validateExhaustiveEffectDependencies
66
import {
77
useCallback,
88
useTransition,
@@ -21,6 +21,24 @@ function useFoo() {
2121
const [v, dispatch] = useReducer(() => {}, null);
2222
const [isPending, dispatchAction] = useActionState(() => {}, null);
2323

24+
useEffect(() => {
25+
dispatch();
26+
startTransition(() => {});
27+
addOptimistic();
28+
setState(null);
29+
dispatchAction();
30+
ref.current = true;
31+
}, [
32+
// intentionally adding unnecessary deps on nonreactive stable values
33+
// to check that they're allowed
34+
dispatch,
35+
startTransition,
36+
addOptimistic,
37+
setState,
38+
dispatchAction,
39+
ref,
40+
]);
41+
2442
return useCallback(() => {
2543
dispatch();
2644
startTransition(() => {});
@@ -50,7 +68,7 @@ export const FIXTURE_ENTRYPOINT = {
5068
## Code
5169

5270
```javascript
53-
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies
71+
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies @validateExhaustiveEffectDependencies
5472
import {
5573
useCallback,
5674
useTransition,
@@ -62,14 +80,15 @@ import {
6280
} from "react";
6381

6482
function useFoo() {
65-
const $ = _c(1);
83+
const $ = _c(3);
6684
const [, setState] = useState();
6785
const ref = useRef(null);
6886
const [, startTransition] = useTransition();
6987
const [, addOptimistic] = useOptimistic();
7088
const [, dispatch] = useReducer(_temp, null);
7189
const [, dispatchAction] = useActionState(_temp2, null);
7290
let t0;
91+
let t1;
7392
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
7493
t0 = () => {
7594
dispatch();
@@ -79,12 +98,38 @@ function useFoo() {
7998
dispatchAction();
8099
ref.current = true;
81100
};
101+
t1 = [
102+
dispatch,
103+
startTransition,
104+
addOptimistic,
105+
setState,
106+
dispatchAction,
107+
ref,
108+
];
82109
$[0] = t0;
110+
$[1] = t1;
83111
} else {
84112
t0 = $[0];
113+
t1 = $[1];
114+
}
115+
useEffect(t0, t1);
116+
let t2;
117+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
118+
t2 = () => {
119+
dispatch();
120+
startTransition(_temp4);
121+
addOptimistic();
122+
setState(null);
123+
dispatchAction();
124+
ref.current = true;
125+
};
126+
$[2] = t2;
127+
} else {
128+
t2 = $[2];
85129
}
86-
return t0;
130+
return t2;
87131
}
132+
function _temp4() {}
88133
function _temp3() {}
89134
function _temp2() {}
90135
function _temp() {}
@@ -97,4 +142,4 @@ export const FIXTURE_ENTRYPOINT = {
97142
```
98143
99144
### Eval output
100-
(kind: ok) "[[ function params=0 ]]"
145+
(kind: exception) useEffect is not defined

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/exhaustive-deps-allow-nonreactive-stable-types-as-extra-deps.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// @validateExhaustiveMemoizationDependencies
1+
// @validateExhaustiveMemoizationDependencies @validateExhaustiveEffectDependencies
22
import {
33
useCallback,
44
useTransition,
@@ -17,6 +17,24 @@ function useFoo() {
1717
const [v, dispatch] = useReducer(() => {}, null);
1818
const [isPending, dispatchAction] = useActionState(() => {}, null);
1919

20+
useEffect(() => {
21+
dispatch();
22+
startTransition(() => {});
23+
addOptimistic();
24+
setState(null);
25+
dispatchAction();
26+
ref.current = true;
27+
}, [
28+
// intentionally adding unnecessary deps on nonreactive stable values
29+
// to check that they're allowed
30+
dispatch,
31+
startTransition,
32+
addOptimistic,
33+
setState,
34+
dispatchAction,
35+
ref,
36+
]);
37+
2038
return useCallback(() => {
2139
dispatch();
2240
startTransition(() => {});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validateExhaustiveEffectDependencies
6+
import {useEffect} from 'react';
7+
8+
function Component({x, y, z}) {
9+
const effectEvent = useEffectEvent(() => {
10+
log(x);
11+
});
12+
13+
// ok - effectEvent not included in deps
14+
useEffect(() => {
15+
effectEvent();
16+
}, []);
17+
}
18+
19+
```
20+
21+
## Code
22+
23+
```javascript
24+
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveEffectDependencies
25+
import { useEffect } from "react";
26+
27+
function Component(t0) {
28+
const $ = _c(5);
29+
const { x } = t0;
30+
let t1;
31+
if ($[0] !== x) {
32+
t1 = () => {
33+
log(x);
34+
};
35+
$[0] = x;
36+
$[1] = t1;
37+
} else {
38+
t1 = $[1];
39+
}
40+
const effectEvent = useEffectEvent(t1);
41+
let t2;
42+
if ($[2] !== effectEvent) {
43+
t2 = () => {
44+
effectEvent();
45+
};
46+
$[2] = effectEvent;
47+
$[3] = t2;
48+
} else {
49+
t2 = $[3];
50+
}
51+
let t3;
52+
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
53+
t3 = [];
54+
$[4] = t3;
55+
} else {
56+
t3 = $[4];
57+
}
58+
useEffect(t2, t3);
59+
}
60+
61+
```
62+
63+
### Eval output
64+
(kind: exception) Fixture not implemented
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @validateExhaustiveEffectDependencies
2+
import {useEffect, useEffectEvent} from 'react';
3+
4+
function Component({x, y, z}) {
5+
const effectEvent = useEffectEvent(() => {
6+
log(x);
7+
});
8+
9+
// ok - effectEvent not included in deps
10+
useEffect(() => {
11+
effectEvent();
12+
}, []);
13+
}

0 commit comments

Comments
 (0)