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