@samitouri / QOS-React / commits / 9180a37fba

[compiler] Allow inferred non-optional paths when manual deps were optional

If the inferred deps are more precise (non-optional) than the manual deps (optional) it should pass validation. The other direction also seems like it would be fine - inferring optional deps when the original was non-optional - but for now let's keep the "at least as precise" rule. ghstack-source-id: 9f7a99ee5f7caa2c2d96f70f360e4320bac3de2d Pull Request resolved: https://github.com/facebook/react/pull/30816

Joe Savona committed Aug 28, 2024 at 10:52 UTC 9180a37fba0c9ad642bfc6e1c2839f88f66485ab
3 files changed +67 -4
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts
+8 -4
@@ -167,12 +167,16 @@ function compareDeps(
167
168 let isSubpath = true;
169 for (let i = 0; i < Math.min(inferred.path.length, source.path.length); i++) {
170 - if (
171 - inferred.path[i].property !== source.path[i].property ||
172 - inferred.path[i].optional !== source.path[i].optional
173 - ) {
170 + if (inferred.path[i].property !== source.path[i].property) {
171 isSubpath = false;
172 break;
173 + } else if (inferred.path[i].optional && !source.path[i].optional) {
174 + /**
175 + * The inferred path must be at least as precise as the manual path:
176 + * if the inferred path is optional, then the source path must have
177 + * been optional too.
178 + */
179 + return CompareDependencyResult.PathDifference;
180 }
181 }
182
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-as-memo-dep-non-optional-in-body.expect.md new
+50
@@ -0,0 +1,50 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validatePreserveExistingMemoizationGuarantees
6 +function Component(props) {
7 + const data = useMemo(() => {
8 + // actual code is non-optional
9 + return props.items.edges.nodes ?? [];
10 + // deps are optional
11 + }, [props.items?.edges?.nodes]);
12 + return <Foo data={data} />;
13 +}
14 +
15 +```
16 +
17 +## Code
18 +
19 +```javascript
20 +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
21 +function Component(props) {
22 + const $ = _c(4);
23 +
24 + props.items?.edges?.nodes;
25 + let t0;
26 + let t1;
27 + if ($[0] !== props.items.edges.nodes) {
28 + t1 = props.items.edges.nodes ?? [];
29 + $[0] = props.items.edges.nodes;
30 + $[1] = t1;
31 + } else {
32 + t1 = $[1];
33 + }
34 + t0 = t1;
35 + const data = t0;
36 + let t2;
37 + if ($[2] !== data) {
38 + t2 = <Foo data={data} />;
39 + $[2] = data;
40 + $[3] = t2;
41 + } else {
42 + t2 = $[3];
43 + }
44 + return t2;
45 +}
46 +
47 +```
48 +
49 +### Eval output
50 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/optional-member-expression-as-memo-dep-non-optional-in-body.js new
+9
@@ -0,0 +1,9 @@
1 +// @validatePreserveExistingMemoizationGuarantees
2 +function Component(props) {
3 + const data = useMemo(() => {
4 + // actual code is non-optional
5 + return props.items.edges.nodes ?? [];
6 + // deps are optional
7 + }, [props.items?.edges?.nodes]);
8 + return <Foo data={data} />;
9 +}