@samitouri / QOS-React / commits / c03f1c809a

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> {