@samitouri / QOS-React / commits / 8a53160111

[compiler] Don't error on ref-in-render on StartMemoize

Test Plan: Fixes the previous issue: ref enforcement ignores memoization marker instructions ghstack-source-id: f35d6a611c5e740e9ea354ec80c3d7cdb3c0d658 Pull Request resolved: https://github.com/facebook/react/pull/30715

Mike Vitousek committed Aug 16, 2024 at 13:27 UTC 8a531601115927400aa04d26a5f1800d159e1e7e
6 files changed +97 -74
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccesInRender.ts
+3
@@ -185,6 +185,9 @@ function validateNoRefAccessInRenderImpl(
185 }
186 break;
187 }
188 + case 'StartMemoize':
189 + case 'FinishMemoize':
190 + break;
191 default: {
192 for (const operand of eachInstructionValueOperand(instr.value)) {
193 validateNoRefValueAccess(errors, refAccessingFunctions, operand);
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-useCallback-set-ref-nested-property-ref-modified-later-preserve-memoization.expect.md
+5 -15
@@ -31,23 +31,13 @@ export const FIXTURE_ENTRYPOINT = {
31 ## Error
32
33 ```
34 - 5 | const ref = useRef({inner: null});
35 - 6 |
36 -> 7 | const onChange = useCallback(event => {
37 - | ^^^^^^^^^^
38 -> 8 | // The ref should still be mutable here even though function deps are frozen in
39 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40 -> 9 | // @enablePreserveExistingMemoizationGuarantees mode
41 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42 -> 10 | ref.current.inner = event.target.value;
43 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44 -> 11 | });
45 - | ^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $44:TObject<BuiltInFunction> (7:11)
46 -
47 -InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (14:14)
34 12 |
35 13 | // The ref is modified later, extending its range and preventing memoization of onChange
50 - 14 | ref.current.inner = null;
36 +> 14 | ref.current.inner = null;
37 + | ^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (14:14)
38 + 15 |
39 + 16 | return <input onChange={onChange} />;
40 + 17 | }
41 ```
42
43
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-accesses-ref-mutated-later-via-function-preserve-memoization.expect.md
+7 -17
@@ -34,25 +34,15 @@ export const FIXTURE_ENTRYPOINT = {
34 ## Error
35
36 ```
37 - 5 | const ref = useRef({inner: null});
38 - 6 |
39 -> 7 | const onChange = useCallback(event => {
40 - | ^^^^^^^^^^
41 -> 8 | // The ref should still be mutable here even though function deps are frozen in
42 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
43 -> 9 | // @enablePreserveExistingMemoizationGuarantees mode
44 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45 -> 10 | ref.current.inner = event.target.value;
46 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
47 -> 11 | });
48 - | ^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $53:TObject<BuiltInFunction> (7:11)
49 -
50 -InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef). Function mutate? $77[20:22]:TObject<BuiltInFunction> accesses a ref (17:17)
37 + 15 | ref.current.inner = null;
38 + 16 | };
39 +> 17 | reset();
40 + | ^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef). Function mutate? $77[20:22]:TObject<BuiltInFunction> accesses a ref (17:17)
41
42 InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (17:17)
53 - 12 |
54 - 13 | // The ref is modified later, extending its range and preventing memoization of onChange
55 - 14 | const reset = () => {
43 + 18 |
44 + 19 | return <input onChange={onChange} />;
45 + 20 | }
46 ```
47
48
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-ref-in-render.expect.md deleted
-41
@@ -1,41 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @flow @validateRefAccessDuringRender @validatePreserveExistingMemoizationGuarantees
6 -
7 -component Foo() {
8 - const ref = useRef();
9 -
10 - const s = useCallback(() => {
11 - return ref.current;
12 - });
13 -
14 - return <a r={s} />;
15 -}
16 -
17 -export const FIXTURE_ENTRYPOINT = {
18 - fn: Foo,
19 - params: [],
20 -};
21 -
22 -```
23 -
24 -
25 -## Error
26 -
27 -```
28 - 4 | const ref = useRef();
29 - 5 |
30 -> 6 | const s = useCallback(() => {
31 - | ^^^^^^^
32 -> 7 | return ref.current;
33 - | ^^^^^^^^^^^^^^^^^^^^^^^
34 -> 8 | });
35 - | ^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at read $27:TObject<BuiltInFunction> (6:8)
36 - 9 |
37 - 10 | return <a r={s} />;
38 - 11 | }
39 -```
40 -
41 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-ref-in-render.expect.md new
+76
@@ -0,0 +1,76 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @flow @validateRefAccessDuringRender @validatePreserveExistingMemoizationGuarantees
6 +import {useCallback, useRef} from 'react';
7 +
8 +component Foo() {
9 + const ref = useRef();
10 +
11 + const s = useCallback(() => {
12 + return ref.current;
13 + });
14 +
15 + return <A r={s} />;
16 +}
17 +
18 +component A(r: mixed) {
19 + return <div />;
20 +}
21 +
22 +export const FIXTURE_ENTRYPOINT = {
23 + fn: Foo,
24 + params: [],
25 +};
26 +
27 +```
28 +
29 +## Code
30 +
31 +```javascript
32 +import { c as _c } from "react/compiler-runtime";
33 +import { useCallback, useRef } from "react";
34 +
35 +function Foo() {
36 + const $ = _c(2);
37 + const ref = useRef();
38 + let t0;
39 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
40 + t0 = () => ref.current;
41 + $[0] = t0;
42 + } else {
43 + t0 = $[0];
44 + }
45 + const s = t0;
46 + let t1;
47 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
48 + t1 = <A r={s} />;
49 + $[1] = t1;
50 + } else {
51 + t1 = $[1];
52 + }
53 + return t1;
54 +}
55 +
56 +function A(t0) {
57 + const $ = _c(1);
58 + let t1;
59 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
60 + t1 = <div />;
61 + $[0] = t1;
62 + } else {
63 + t1 = $[0];
64 + }
65 + return t1;
66 +}
67 +
68 +export const FIXTURE_ENTRYPOINT = {
69 + fn: Foo,
70 + params: [],
71 +};
72 +
73 +```
74 +
75 +### Eval output
76 +(kind: ok) <div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-ref-in-render.js renamed
+6 -1
@@ -1,4 +1,5 @@
1 // @flow @validateRefAccessDuringRender @validatePreserveExistingMemoizationGuarantees
2 +import {useCallback, useRef} from 'react';
3
4 component Foo() {
5 const ref = useRef();
@@ -7,7 +8,11 @@ component Foo() {
8 return ref.current;
9 });
10
10 - return <a r={s} />;
11 + return <A r={s} />;
12 +}
13 +
14 +component A(r: mixed) {
15 + return <div />;
16 }
17
18 export const FIXTURE_ENTRYPOINT = {