Skip to content

Commit f558784

Browse files
committed
Update on "compiler: Represent pruned scopes instead of inlining"
There are a few places where we want to check whether a value actually got memoized, and we currently have to infer this based on values that "should" have a scope and whether a corresponding scope actually exists. This PR adds a new ReactiveStatement variant to model a reactive scope block that was pruned for some reason, and updates all the passes that prune scopes to instead produce this new variant. [ghstack-poisoned]
2 parents 3b32e6c + 61e9604 commit f558784

29 files changed

+813
-112
lines changed

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
isArrayType,
3030
isMutableEffect,
3131
isObjectType,
32+
isRefValueType,
33+
isUseRefType,
3234
} from "../HIR/HIR";
3335
import { FunctionSignature } from "../HIR/ObjectShape";
3436
import {
@@ -521,7 +523,12 @@ class InferenceState {
521523
break;
522524
}
523525
case Effect.Mutate: {
524-
if (valueKind.kind === ValueKind.Context) {
526+
if (
527+
isRefValueType(place.identifier) ||
528+
isUseRefType(place.identifier)
529+
) {
530+
// no-op: refs are validate via ValidateNoRefAccessInRender
531+
} else if (valueKind.kind === ValueKind.Context) {
525532
functionEffect = {
526533
kind: "ContextMutation",
527534
loc: place.loc,
@@ -560,7 +567,12 @@ class InferenceState {
560567
break;
561568
}
562569
case Effect.Store: {
563-
if (valueKind.kind === ValueKind.Context) {
570+
if (
571+
isRefValueType(place.identifier) ||
572+
isUseRefType(place.identifier)
573+
) {
574+
// no-op: refs are validate via ValidateNoRefAccessInRender
575+
} else if (valueKind.kind === ValueKind.Context) {
564576
functionEffect = {
565577
kind: "ContextMutation",
566578
loc: place.loc,
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validateRefAccessDuringRender
6+
import { useRef } from "react";
7+
8+
function Component() {
9+
const ref = useRef(null);
10+
11+
const setRef = () => {
12+
if (ref.current !== null) {
13+
ref.current = "";
14+
}
15+
};
16+
17+
const onClick = () => {
18+
setRef();
19+
};
20+
21+
return (
22+
<>
23+
<input ref={ref} />
24+
<button onClick={onClick} />
25+
</>
26+
);
27+
}
28+
29+
export const FIXTURE_ENTRYPOINT = {
30+
fn: Component,
31+
params: [{}],
32+
};
33+
34+
```
35+
36+
## Code
37+
38+
```javascript
39+
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender
40+
import { useRef } from "react";
41+
42+
function Component() {
43+
const $ = _c(10);
44+
const ref = useRef(null);
45+
let t0;
46+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
47+
t0 = () => {
48+
if (ref.current !== null) {
49+
ref.current = "";
50+
}
51+
};
52+
$[0] = t0;
53+
} else {
54+
t0 = $[0];
55+
}
56+
const setRef = t0;
57+
let t1;
58+
if ($[1] !== setRef) {
59+
t1 = () => {
60+
setRef();
61+
};
62+
$[1] = setRef;
63+
$[2] = t1;
64+
} else {
65+
t1 = $[2];
66+
}
67+
const onClick = t1;
68+
let t2;
69+
if ($[3] !== ref) {
70+
t2 = <input ref={ref} />;
71+
$[3] = ref;
72+
$[4] = t2;
73+
} else {
74+
t2 = $[4];
75+
}
76+
let t3;
77+
if ($[5] !== onClick) {
78+
t3 = <button onClick={onClick} />;
79+
$[5] = onClick;
80+
$[6] = t3;
81+
} else {
82+
t3 = $[6];
83+
}
84+
let t4;
85+
if ($[7] !== t2 || $[8] !== t3) {
86+
t4 = (
87+
<>
88+
{t2}
89+
{t3}
90+
</>
91+
);
92+
$[7] = t2;
93+
$[8] = t3;
94+
$[9] = t4;
95+
} else {
96+
t4 = $[9];
97+
}
98+
return t4;
99+
}
100+
101+
export const FIXTURE_ENTRYPOINT = {
102+
fn: Component,
103+
params: [{}],
104+
};
105+
106+
```
107+
108+
### Eval output
109+
(kind: ok) <input><button></button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @validateRefAccessDuringRender
2+
import { useRef } from "react";
3+
4+
function Component() {
5+
const ref = useRef(null);
6+
7+
const setRef = () => {
8+
if (ref.current !== null) {
9+
ref.current = "";
10+
}
11+
};
12+
13+
const onClick = () => {
14+
setRef();
15+
};
16+
17+
return (
18+
<>
19+
<input ref={ref} />
20+
<button onClick={onClick} />
21+
</>
22+
);
23+
}
24+
25+
export const FIXTURE_ENTRYPOINT = {
26+
fn: Component,
27+
params: [{}],
28+
};
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validateRefAccessDuringRender
6+
import { useRef } from "react";
7+
8+
function Component() {
9+
const ref = useRef(null);
10+
11+
const onClick = () => {
12+
if (ref.current !== null) {
13+
ref.current = "";
14+
}
15+
};
16+
17+
return (
18+
<>
19+
<input ref={ref} />
20+
<button onClick={onClick} />
21+
</>
22+
);
23+
}
24+
25+
export const FIXTURE_ENTRYPOINT = {
26+
fn: Component,
27+
params: [{}],
28+
};
29+
30+
```
31+
32+
## Code
33+
34+
```javascript
35+
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender
36+
import { useRef } from "react";
37+
38+
function Component() {
39+
const $ = _c(8);
40+
const ref = useRef(null);
41+
let t0;
42+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
43+
t0 = () => {
44+
if (ref.current !== null) {
45+
ref.current = "";
46+
}
47+
};
48+
$[0] = t0;
49+
} else {
50+
t0 = $[0];
51+
}
52+
const onClick = t0;
53+
let t1;
54+
if ($[1] !== ref) {
55+
t1 = <input ref={ref} />;
56+
$[1] = ref;
57+
$[2] = t1;
58+
} else {
59+
t1 = $[2];
60+
}
61+
let t2;
62+
if ($[3] !== onClick) {
63+
t2 = <button onClick={onClick} />;
64+
$[3] = onClick;
65+
$[4] = t2;
66+
} else {
67+
t2 = $[4];
68+
}
69+
let t3;
70+
if ($[5] !== t1 || $[6] !== t2) {
71+
t3 = (
72+
<>
73+
{t1}
74+
{t2}
75+
</>
76+
);
77+
$[5] = t1;
78+
$[6] = t2;
79+
$[7] = t3;
80+
} else {
81+
t3 = $[7];
82+
}
83+
return t3;
84+
}
85+
86+
export const FIXTURE_ENTRYPOINT = {
87+
fn: Component,
88+
params: [{}],
89+
};
90+
91+
```
92+
93+
### Eval output
94+
(kind: ok) <input><button></button>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @validateRefAccessDuringRender
2+
import { useRef } from "react";
3+
4+
function Component() {
5+
const ref = useRef(null);
6+
7+
const onClick = () => {
8+
if (ref.current !== null) {
9+
ref.current = "";
10+
}
11+
};
12+
13+
return (
14+
<>
15+
<input ref={ref} />
16+
<button onClick={onClick} />
17+
</>
18+
);
19+
}
20+
21+
export const FIXTURE_ENTRYPOINT = {
22+
fn: Component,
23+
params: [{}],
24+
};

0 commit comments

Comments
 (0)