[compiler] Make ref enforcement on by default
Summary: The change earlier in this stack makes it less safe to have ref enforcement disabled. This diff enables it by default. ghstack-source-id: d3ab5f1b28b7aed0f0d6d69547bb638a1e326b66 Pull Request resolved: https://github.com/facebook/react/pull/30716
Mike Vitousek committed
Aug 16, 2024 at 13:27 UTC
5edbe29dbe945d821021a1152b267f5a86efc55b
22 files changed
+275
-417
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+1
-1
@@ -223,7 +223,7 @@ const EnvironmentConfigSchema = z.object({
223
validateHooksUsage: z.boolean().default(true),
224
225
// Validate that ref values (`ref.current`) are not accessed during render.
226
- validateRefAccessDuringRender: z.boolean().default(false),
226
+ validateRefAccessDuringRender: z.boolean().default(true),
227
228
/*
229
* Validates that setState is not unconditionally called during render, as it can lead to
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md
deleted
-69
@@ -1,69 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import {useRef} from 'react';
6
-import {addOne} from 'shared-runtime';
7
-
8
-function useKeyCommand() {
9
- const currentPosition = useRef(0);
10
- const handleKey = direction => () => {
11
- const position = currentPosition.current;
12
- const nextPosition = direction === 'left' ? addOne(position) : position;
13
- currentPosition.current = nextPosition;
14
- };
15
- const moveLeft = {
16
- handler: handleKey('left'),
17
- };
18
- const moveRight = {
19
- handler: handleKey('right'),
20
- };
21
- return [moveLeft, moveRight];
22
-}
23
-
24
-export const FIXTURE_ENTRYPOINT = {
25
- fn: useKeyCommand,
26
- params: [],
27
-};
28
-
29
-```
30
-
31
-## Code
32
-
33
-```javascript
34
-import { c as _c } from "react/compiler-runtime";
35
-import { useRef } from "react";
36
-import { addOne } from "shared-runtime";
37
-
38
-function useKeyCommand() {
39
- const $ = _c(1);
40
- const currentPosition = useRef(0);
41
- let t0;
42
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
43
- const handleKey = (direction) => () => {
44
- const position = currentPosition.current;
45
- const nextPosition = direction === "left" ? addOne(position) : position;
46
- currentPosition.current = nextPosition;
47
- };
48
-
49
- const moveLeft = { handler: handleKey("left") };
50
-
51
- const moveRight = { handler: handleKey("right") };
52
-
53
- t0 = [moveLeft, moveRight];
54
- $[0] = t0;
55
- } else {
56
- t0 = $[0];
57
- }
58
- return t0;
59
-}
60
-
61
-export const FIXTURE_ENTRYPOINT = {
62
- fn: useKeyCommand,
63
- params: [],
64
-};
65
-
66
-```
67
-
68
-### Eval output
69
-(kind: ok) [{"handler":"[[ function params=0 ]]"},{"handler":"[[ function params=0 ]]"}]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md
new
+50
@@ -0,0 +1,50 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {useRef} from 'react';
6
+import {addOne} from 'shared-runtime';
7
+
8
+function useKeyCommand() {
9
+ const currentPosition = useRef(0);
10
+ const handleKey = direction => () => {
11
+ const position = currentPosition.current;
12
+ const nextPosition = direction === 'left' ? addOne(position) : position;
13
+ currentPosition.current = nextPosition;
14
+ };
15
+ const moveLeft = {
16
+ handler: handleKey('left'),
17
+ };
18
+ const moveRight = {
19
+ handler: handleKey('right'),
20
+ };
21
+ return [moveLeft, moveRight];
22
+}
23
+
24
+export const FIXTURE_ENTRYPOINT = {
25
+ fn: useKeyCommand,
26
+ params: [],
27
+};
28
+
29
+```
30
+
31
+
32
+## Error
33
+
34
+```
35
+ 10 | };
36
+ 11 | const moveLeft = {
37
+> 12 | handler: handleKey('left'),
38
+ | ^^^^^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12)
39
+
40
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12)
41
+
42
+InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15)
43
+
44
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15)
45
+ 13 | };
46
+ 14 | const moveRight = {
47
+ 15 | handler: handleKey('right'),
48
+```
49
+
50
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.tsx
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md
new
+40
@@ -0,0 +1,40 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
6
+
7
+function Foo(props, ref) {
8
+ const value = {};
9
+ if (CONST_TRUE) {
10
+ mutate(value);
11
+ return <Stringify ref={ref} />;
12
+ }
13
+ mutate(value);
14
+ if (CONST_TRUE) {
15
+ return <Stringify ref={identity(ref)} />;
16
+ }
17
+ return value;
18
+}
19
+
20
+export const FIXTURE_ENTRYPOINT = {
21
+ fn: Foo,
22
+ params: [{}, {current: 'fake-ref-object'}],
23
+};
24
+
25
+```
26
+
27
+
28
+## Error
29
+
30
+```
31
+ 9 | mutate(value);
32
+ 10 | if (CONST_TRUE) {
33
+> 11 | return <Stringify ref={identity(ref)} />;
34
+ | ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (11:11)
35
+ 12 | }
36
+ 13 | return value;
37
+ 14 | }
38
+```
39
+
40
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.tsx
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md
new
+42
@@ -0,0 +1,42 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @enablePreserveExistingMemoizationGuarantees:false
6
+import {useCallback, useRef} from 'react';
7
+
8
+function Component(props) {
9
+ const ref = useRef({inner: null});
10
+
11
+ const onChange = useCallback(event => {
12
+ // The ref should still be mutable here even though function deps are frozen in
13
+ // @enablePreserveExistingMemoizationGuarantees mode
14
+ ref.current.inner = event.target.value;
15
+ });
16
+
17
+ ref.current.inner = null;
18
+
19
+ return <input onChange={onChange} />;
20
+}
21
+
22
+export const FIXTURE_ENTRYPOINT = {
23
+ fn: Component,
24
+ params: [{}],
25
+};
26
+
27
+```
28
+
29
+
30
+## Error
31
+
32
+```
33
+ 11 | });
34
+ 12 |
35
+> 13 | ref.current.inner = null;
36
+ | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
37
+ 14 |
38
+ 15 | return <input onChange={onChange} />;
39
+ 16 | }
40
+```
41
+
42
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.js
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md
deleted
-70
@@ -1,70 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @enableReactiveScopesInHIR:false
6
-import {useRef} from 'react';
7
-import {addOne} from 'shared-runtime';
8
-
9
-function useKeyCommand() {
10
- const currentPosition = useRef(0);
11
- const handleKey = direction => () => {
12
- const position = currentPosition.current;
13
- const nextPosition = direction === 'left' ? addOne(position) : position;
14
- currentPosition.current = nextPosition;
15
- };
16
- const moveLeft = {
17
- handler: handleKey('left'),
18
- };
19
- const moveRight = {
20
- handler: handleKey('right'),
21
- };
22
- return [moveLeft, moveRight];
23
-}
24
-
25
-export const FIXTURE_ENTRYPOINT = {
26
- fn: useKeyCommand,
27
- params: [],
28
-};
29
-
30
-```
31
-
32
-## Code
33
-
34
-```javascript
35
-import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
36
-import { useRef } from "react";
37
-import { addOne } from "shared-runtime";
38
-
39
-function useKeyCommand() {
40
- const $ = _c(1);
41
- const currentPosition = useRef(0);
42
- let t0;
43
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
44
- const handleKey = (direction) => () => {
45
- const position = currentPosition.current;
46
- const nextPosition = direction === "left" ? addOne(position) : position;
47
- currentPosition.current = nextPosition;
48
- };
49
-
50
- const moveLeft = { handler: handleKey("left") };
51
-
52
- const moveRight = { handler: handleKey("right") };
53
-
54
- t0 = [moveLeft, moveRight];
55
- $[0] = t0;
56
- } else {
57
- t0 = $[0];
58
- }
59
- return t0;
60
-}
61
-
62
-export const FIXTURE_ENTRYPOINT = {
63
- fn: useKeyCommand,
64
- params: [],
65
-};
66
-
67
-```
68
-
69
-### Eval output
70
-(kind: ok) [{"handler":"[[ function params=0 ]]"},{"handler":"[[ function params=0 ]]"}]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md
new
+51
@@ -0,0 +1,51 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @enableReactiveScopesInHIR:false
6
+import {useRef} from 'react';
7
+import {addOne} from 'shared-runtime';
8
+
9
+function useKeyCommand() {
10
+ const currentPosition = useRef(0);
11
+ const handleKey = direction => () => {
12
+ const position = currentPosition.current;
13
+ const nextPosition = direction === 'left' ? addOne(position) : position;
14
+ currentPosition.current = nextPosition;
15
+ };
16
+ const moveLeft = {
17
+ handler: handleKey('left'),
18
+ };
19
+ const moveRight = {
20
+ handler: handleKey('right'),
21
+ };
22
+ return [moveLeft, moveRight];
23
+}
24
+
25
+export const FIXTURE_ENTRYPOINT = {
26
+ fn: useKeyCommand,
27
+ params: [],
28
+};
29
+
30
+```
31
+
32
+
33
+## Error
34
+
35
+```
36
+ 11 | };
37
+ 12 | const moveLeft = {
38
+> 13 | handler: handleKey('left'),
39
+ | ^^^^^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
40
+
41
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
42
+
43
+InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16)
44
+
45
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16)
46
+ 14 | };
47
+ 15 | const moveRight = {
48
+ 16 | handler: handleKey('right'),
49
+```
50
+
51
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.tsx
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md
new
+35
@@ -0,0 +1,35 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @validatePreserveExistingMemoizationGuarantees:true
6
+
7
+import {useRef, useMemo} from 'react';
8
+import {makeArray} from 'shared-runtime';
9
+
10
+function useFoo() {
11
+ const r = useRef();
12
+ return useMemo(() => makeArray(r), []);
13
+}
14
+
15
+export const FIXTURE_ENTRYPOINT = {
16
+ fn: useFoo,
17
+ params: [],
18
+};
19
+
20
+```
21
+
22
+
23
+## Error
24
+
25
+```
26
+ 6 | function useFoo() {
27
+ 7 | const r = useRef();
28
+> 8 | return useMemo(() => makeArray(r), []);
29
+ | ^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (8:8)
30
+ 9 | }
31
+ 10 |
32
+ 11 | export const FIXTURE_ENTRYPOINT = {
33
+```
34
+
35
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.ts
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md
new
+31
@@ -0,0 +1,31 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @flow @validatePreserveExistingMemoizationGuarantees
6
+import {identity} from 'shared-runtime';
7
+
8
+component Component(disableLocalRef, ref) {
9
+ const localRef = useFooRef();
10
+ const mergedRef = useMemo(() => {
11
+ return disableLocalRef ? ref : identity(ref, localRef);
12
+ }, [disableLocalRef, ref, localRef]);
13
+ return <div ref={mergedRef} />;
14
+}
15
+
16
+```
17
+
18
+
19
+## Error
20
+
21
+```
22
+ 5 | const localRef = useFooRef();
23
+ 6 | const mergedRef = useMemo(() => {
24
+> 7 | return disableLocalRef ? ref : identity(ref, localRef);
25
+ | ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (7:7)
26
+ 8 | }, [disableLocalRef, ref, localRef]);
27
+ 9 | return <div ref={mergedRef} />;
28
+ 10 | }
29
+```
30
+
31
+
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.js
renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md
deleted
-53
@@ -1,53 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @validatePreserveExistingMemoizationGuarantees:true
6
-
7
-import {useRef, useMemo} from 'react';
8
-import {makeArray} from 'shared-runtime';
9
-
10
-function useFoo() {
11
- const r = useRef();
12
- return useMemo(() => makeArray(r), []);
13
-}
14
-
15
-export const FIXTURE_ENTRYPOINT = {
16
- fn: useFoo,
17
- params: [],
18
-};
19
-
20
-```
21
-
22
-## Code
23
-
24
-```javascript
25
-import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees:true
26
-
27
-import { useRef, useMemo } from "react";
28
-import { makeArray } from "shared-runtime";
29
-
30
-function useFoo() {
31
- const $ = _c(1);
32
- const r = useRef();
33
- let t0;
34
- let t1;
35
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
36
- t1 = makeArray(r);
37
- $[0] = t1;
38
- } else {
39
- t1 = $[0];
40
- }
41
- t0 = t1;
42
- return t0;
43
-}
44
-
45
-export const FIXTURE_ENTRYPOINT = {
46
- fn: useFoo,
47
- params: [],
48
-};
49
-
50
-```
51
-
52
-### Eval output
53
-(kind: ok) [{}]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md
deleted
-54
@@ -1,54 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @flow @validatePreserveExistingMemoizationGuarantees
6
-import {identity} from 'shared-runtime';
7
-
8
-component Component(disableLocalRef, ref) {
9
- const localRef = useFooRef();
10
- const mergedRef = useMemo(() => {
11
- return disableLocalRef ? ref : identity(ref, localRef);
12
- }, [disableLocalRef, ref, localRef]);
13
- return <div ref={mergedRef} />;
14
-}
15
-
16
-```
17
-
18
-## Code
19
-
20
-```javascript
21
-import { c as _c } from "react/compiler-runtime";
22
-import { identity } from "shared-runtime";
23
-
24
-const Component = React.forwardRef(Component_withRef);
25
-function Component_withRef(t0, ref) {
26
- const $ = _c(6);
27
- const { disableLocalRef } = t0;
28
- const localRef = useFooRef();
29
- let t1;
30
- let t2;
31
- if ($[0] !== disableLocalRef || $[1] !== ref || $[2] !== localRef) {
32
- t2 = disableLocalRef ? ref : identity(ref, localRef);
33
- $[0] = disableLocalRef;
34
- $[1] = ref;
35
- $[2] = localRef;
36
- $[3] = t2;
37
- } else {
38
- t2 = $[3];
39
- }
40
- t1 = t2;
41
- const mergedRef = t1;
42
- let t3;
43
- if ($[4] !== mergedRef) {
44
- t3 = <div ref={mergedRef} />;
45
- $[4] = mergedRef;
46
- $[5] = t3;
47
- } else {
48
- t3 = $[5];
49
- }
50
- return t3;
51
-}
52
-
53
-```
54
-
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md
deleted
-89
@@ -1,89 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
6
-
7
-function Foo(props, ref) {
8
- const value = {};
9
- if (CONST_TRUE) {
10
- mutate(value);
11
- return <Stringify ref={ref} />;
12
- }
13
- mutate(value);
14
- if (CONST_TRUE) {
15
- return <Stringify ref={identity(ref)} />;
16
- }
17
- return value;
18
-}
19
-
20
-export const FIXTURE_ENTRYPOINT = {
21
- fn: Foo,
22
- params: [{}, {current: 'fake-ref-object'}],
23
-};
24
-
25
-```
26
-
27
-## Code
28
-
29
-```javascript
30
-import { c as _c } from "react/compiler-runtime";
31
-import { Stringify, identity, mutate, CONST_TRUE } from "shared-runtime";
32
-
33
-function Foo(props, ref) {
34
- const $ = _c(7);
35
- let value;
36
- let t0;
37
- if ($[0] !== ref) {
38
- t0 = Symbol.for("react.early_return_sentinel");
39
- bb0: {
40
- value = {};
41
- if (CONST_TRUE) {
42
- mutate(value);
43
- t0 = <Stringify ref={ref} />;
44
- break bb0;
45
- }
46
-
47
- mutate(value);
48
- }
49
- $[0] = ref;
50
- $[1] = value;
51
- $[2] = t0;
52
- } else {
53
- value = $[1];
54
- t0 = $[2];
55
- }
56
- if (t0 !== Symbol.for("react.early_return_sentinel")) {
57
- return t0;
58
- }
59
- if (CONST_TRUE) {
60
- let t1;
61
- if ($[3] !== ref) {
62
- t1 = identity(ref);
63
- $[3] = ref;
64
- $[4] = t1;
65
- } else {
66
- t1 = $[4];
67
- }
68
- let t2;
69
- if ($[5] !== t1) {
70
- t2 = <Stringify ref={t1} />;
71
- $[5] = t1;
72
- $[6] = t2;
73
- } else {
74
- t2 = $[6];
75
- }
76
- return t2;
77
- }
78
- return value;
79
-}
80
-
81
-export const FIXTURE_ENTRYPOINT = {
82
- fn: Foo,
83
- params: [{}, { current: "fake-ref-object" }],
84
-};
85
-
86
-```
87
-
88
-### Eval output
89
-(kind: ok) <div>{"ref":{"current":"fake-ref-object"}}</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md
deleted
-75
@@ -1,75 +0,0 @@
1
-
2
-## Input
3
-
4
-```javascript
5
-// @enablePreserveExistingMemoizationGuarantees:false
6
-import {useCallback, useRef} from 'react';
7
-
8
-function Component(props) {
9
- const ref = useRef({inner: null});
10
-
11
- const onChange = useCallback(event => {
12
- // The ref should still be mutable here even though function deps are frozen in
13
- // @enablePreserveExistingMemoizationGuarantees mode
14
- ref.current.inner = event.target.value;
15
- });
16
-
17
- ref.current.inner = null;
18
-
19
- return <input onChange={onChange} />;
20
-}
21
-
22
-export const FIXTURE_ENTRYPOINT = {
23
- fn: Component,
24
- params: [{}],
25
-};
26
-
27
-```
28
-
29
-## Code
30
-
31
-```javascript
32
-import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
33
-import { useCallback, useRef } from "react";
34
-
35
-function Component(props) {
36
- const $ = _c(3);
37
- let t0;
38
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
39
- t0 = { inner: null };
40
- $[0] = t0;
41
- } else {
42
- t0 = $[0];
43
- }
44
- const ref = useRef(t0);
45
- let t1;
46
- if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
47
- t1 = (event) => {
48
- ref.current.inner = event.target.value;
49
- };
50
- $[1] = t1;
51
- } else {
52
- t1 = $[1];
53
- }
54
- const onChange = t1;
55
-
56
- ref.current.inner = null;
57
- let t2;
58
- if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
59
- t2 = <input onChange={onChange} />;
60
- $[2] = t2;
61
- } else {
62
- t2 = $[2];
63
- }
64
- return t2;
65
-}
66
-
67
-export const FIXTURE_ENTRYPOINT = {
68
- fn: Component,
69
- params: [{}],
70
-};
71
-
72
-```
73
-
74
-### Eval output
75
-(kind: ok) <input>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts
+3
-3
@@ -14,16 +14,16 @@ describe('parseConfigPragma()', () => {
14
// Validate defaults first to make sure that the parser is getting the value from the pragma,
15
// and not just missing it and getting the default value
16
expect(defaultConfig.enableUseTypeAnnotations).toBe(false);
17
- expect(defaultConfig.validateRefAccessDuringRender).toBe(false);
17
+ expect(defaultConfig.validateNoSetStateInPassiveEffects).toBe(false);
18
expect(defaultConfig.validateNoSetStateInRender).toBe(true);
19
20
const config = parseConfigPragma(
21
- '@enableUseTypeAnnotations @validateRefAccessDuringRender:true @validateNoSetStateInRender:false',
21
+ '@enableUseTypeAnnotations @validateNoSetStateInPassiveEffects:true @validateNoSetStateInRender:false',
22
);
23
expect(config).toEqual({
24
...defaultConfig,
25
enableUseTypeAnnotations: true,
26
- validateRefAccessDuringRender: true,
26
+ validateNoSetStateInPassiveEffects: true,
27
validateNoSetStateInRender: false,
28
});
29
});
compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts
+18
-2
@@ -93,12 +93,12 @@ const tests: CompilerTestCases = {
93
`,
94
},
95
{
96
- // TODO(gsn): Move this to invalid test suite, when we turn on
97
- // validateRefAccessDuringRender validation
96
+ // Don't report the issue if Flow already has
97
name: '[InvalidInput] Ref access during render',
98
code: normalizeIndent`
99
function Component(props) {
100
const ref = useRef(null);
101
+ // $FlowFixMe[react-rule-unsafe-ref]
102
const value = ref.current;
103
return value;
104
}
@@ -106,6 +106,22 @@ const tests: CompilerTestCases = {
106
},
107
],
108
invalid: [
109
+ {
110
+ name: '[InvalidInput] Ref access during render',
111
+ code: normalizeIndent`
112
+ function Component(props) {
113
+ const ref = useRef(null);
114
+ const value = ref.current;
115
+ return value;
116
+ }
117
+ `,
118
+ errors: [
119
+ {
120
+ message:
121
+ 'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)',
122
+ },
123
+ ],
124
+ },
125
{
126
name: 'Reportable levels can be configured',
127
options: [{reportableLevels: new Set([ErrorSeverity.Todo])}],
compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts
+4
-1
@@ -179,7 +179,10 @@ const rule: Rule.RuleModule = {
179
if (!isReportableDiagnostic(detail)) {
180
return;
181
}
182
- if (hasFlowSuppression(detail.loc, 'react-rule-hook')) {
182
+ if (
183
+ hasFlowSuppression(detail.loc, 'react-rule-hook') ||
184
+ hasFlowSuppression(detail.loc, 'react-rule-unsafe-ref')
185
+ ) {
186
// If Flow already caught this error, we don't need to report it again.
187
return;
188
}