@samitouri / QOS-React / commits / 5edbe29dbe

[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 }