Extract shared-runtime helper for validating memoization
Extracts a helper from the repro earlier in the stack into a helper in shared-runtime. This makes it easy to verify that memoization is actually working.
Joe Savona committed
Mar 19, 2024 at 13:28 UTC
c03f1c809aeaf69ed26eda39df9dbd7242bc014d
3 files changed
+68
-95
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-unreachable-code-early-return-in-useMemo.expect.md
+41
-69
@@ -4,34 +4,10 @@
4
```javascript
5
// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
6
import { useMemo, useState } from "react";
7
-import { Stringify, identity } from "shared-runtime";
7
+import { ValidateMemoization, identity } from "shared-runtime";
8
9
function Component({ value }) {
10
- "use no forget";
11
- const result = useValue(value);
12
- return <Validate inputs={[value]} output={result} />;
13
-}
14
-
15
-function Validate({ inputs, output }) {
16
- "use no forget";
17
- const [previousInputs, setPreviousInputs] = useState(inputs);
18
- const [previousOutput, setPreviousOutput] = useState(output);
19
- if (
20
- inputs.length !== previousInputs.length ||
21
- inputs.some((item, i) => item !== previousInputs[i])
22
- ) {
23
- // Some input changed, we expect the output to change
24
- setPreviousInputs(inputs);
25
- setPreviousOutput(output);
26
- } else if (output !== previousOutput) {
27
- // Else output should be stable
28
- throw new Error("Output identity changed but inputs did not");
29
- }
30
- return <Stringify inputs={inputs} output={output} />;
31
-}
32
-
33
-function useValue(value) {
34
- return useMemo(() => {
10
+ const result = useMemo(() => {
11
if (value == null) {
12
return null;
13
}
@@ -41,6 +17,7 @@ function useValue(value) {
17
return null;
18
}
19
}, [value]);
20
+ return <ValidateMemoization inputs={[value]} output={result} />;
21
}
22
23
export const FIXTURE_ENTRYPOINT = {
@@ -69,70 +46,65 @@ import {
46
useState,
47
unstable_useMemoCache as useMemoCache,
48
} from "react";
72
-import { Stringify, identity } from "shared-runtime";
49
+import { ValidateMemoization, identity } from "shared-runtime";
50
74
-function Component({ value }) {
75
- "use no forget";
76
- const result = useValue(value);
77
- return <Validate inputs={[value]} output={result} />;
78
-}
79
-
80
-function Validate({ inputs, output }) {
81
- "use no forget";
82
- const [previousInputs, setPreviousInputs] = useState(inputs);
83
- const [previousOutput, setPreviousOutput] = useState(output);
84
- if (
85
- inputs.length !== previousInputs.length ||
86
- inputs.some((item, i) => item !== previousInputs[i])
87
- ) {
88
- // Some input changed, we expect the output to change
89
- setPreviousInputs(inputs);
90
- setPreviousOutput(output);
91
- } else if (output !== previousOutput) {
92
- // Else output should be stable
93
- throw new Error("Output identity changed but inputs did not");
94
- }
95
- return <Stringify inputs={inputs} output={output} />;
96
-}
97
-
98
-function useValue(value) {
99
- const $ = useMemoCache(5);
100
- let t0;
51
+function Component(t0) {
52
+ const $ = useMemoCache(10);
53
+ const { value } = t0;
54
+ let t1;
55
bb13: {
56
if (value == null) {
57
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
104
- t0 = null;
58
+ t1 = null;
59
break bb13;
106
- $[0] = t0;
60
+ $[0] = t1;
61
} else {
108
- t0 = $[0];
62
+ t1 = $[0];
63
}
64
}
65
try {
112
- let t2;
66
+ let t3;
67
if ($[1] !== value) {
114
- t2 = { value };
68
+ t3 = { value };
69
$[1] = value;
116
- $[2] = t2;
70
+ $[2] = t3;
71
} else {
118
- t2 = $[2];
72
+ t3 = $[2];
73
}
74
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
121
- t0 = t2;
122
- $[3] = t0;
75
+ t1 = t3;
76
+ $[3] = t1;
77
} else {
124
- t0 = $[3];
78
+ t1 = $[3];
79
}
126
- } catch (t1) {
80
+ } catch (t2) {
81
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
128
- t0 = null;
129
- $[4] = t0;
82
+ t1 = null;
83
+ $[4] = t1;
84
} else {
131
- t0 = $[4];
85
+ t1 = $[4];
86
}
87
}
88
}
135
- return t0;
89
+ const result = t1;
90
+ let t2;
91
+ if ($[5] !== value) {
92
+ t2 = [value];
93
+ $[5] = value;
94
+ $[6] = t2;
95
+ } else {
96
+ t2 = $[6];
97
+ }
98
+ let t3;
99
+ if ($[7] !== t2 || $[8] !== result) {
100
+ t3 = <ValidateMemoization inputs={t2} output={result} />;
101
+ $[7] = t2;
102
+ $[8] = result;
103
+ $[9] = t3;
104
+ } else {
105
+ t3 = $[9];
106
+ }
107
+ return t3;
108
}
109
110
export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/repro-unreachable-code-early-return-in-useMemo.js
+3
-26
@@ -1,33 +1,9 @@
1
// @enableAssumeHooksFollowRulesOfReact @enableTransitivelyFreezeFunctionExpressions
2
import { useMemo, useState } from "react";
3
-import { Stringify, identity } from "shared-runtime";
3
+import { ValidateMemoization, identity } from "shared-runtime";
4
5
function Component({ value }) {
6
- "use no forget";
7
- const result = useValue(value);
8
- return <Validate inputs={[value]} output={result} />;
9
-}
10
-
11
-function Validate({ inputs, output }) {
12
- "use no forget";
13
- const [previousInputs, setPreviousInputs] = useState(inputs);
14
- const [previousOutput, setPreviousOutput] = useState(output);
15
- if (
16
- inputs.length !== previousInputs.length ||
17
- inputs.some((item, i) => item !== previousInputs[i])
18
- ) {
19
- // Some input changed, we expect the output to change
20
- setPreviousInputs(inputs);
21
- setPreviousOutput(output);
22
- } else if (output !== previousOutput) {
23
- // Else output should be stable
24
- throw new Error("Output identity changed but inputs did not");
25
- }
26
- return <Stringify inputs={inputs} output={output} />;
27
-}
28
-
29
-function useValue(value) {
30
- return useMemo(() => {
6
+ const result = useMemo(() => {
7
if (value == null) {
8
return null;
9
}
@@ -37,6 +13,7 @@ function useValue(value) {
13
return null;
14
}
15
}, [value]);
16
+ return <ValidateMemoization inputs={[value]} output={result} />;
17
}
18
19
export const FIXTURE_ENTRYPOINT = {
compiler/packages/snap/src/sprout/shared-runtime.ts
+24
@@ -226,6 +226,30 @@ export function Stringify(props: any): React.ReactElement {
226
);
227
}
228
229
+export function ValidateMemoization({
230
+ inputs,
231
+ output,
232
+}: {
233
+ inputs: Array<any>;
234
+ output: any;
235
+}) {
236
+ "use no forget";
237
+ const [previousInputs, setPreviousInputs] = React.useState(inputs);
238
+ const [previousOutput, setPreviousOutput] = React.useState(output);
239
+ if (
240
+ inputs.length !== previousInputs.length ||
241
+ inputs.some((item, i) => item !== previousInputs[i])
242
+ ) {
243
+ // Some input changed, we expect the output to change
244
+ setPreviousInputs(inputs);
245
+ setPreviousOutput(output);
246
+ } else if (output !== previousOutput) {
247
+ // Else output should be stable
248
+ throw new Error("Output identity changed but inputs did not");
249
+ }
250
+ return React.createElement(Stringify, { inputs, output });
251
+}
252
+
253
export function createHookWrapper<TProps, TRet>(
254
useMaybeHook: (props: TProps) => TRet
255
): FunctionComponent<TProps> {