diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
index 7eef0a7cc24b..ca03b8a7b1e3 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
@@ -223,7 +223,7 @@ const EnvironmentConfigSchema = z.object({
validateHooksUsage: z.boolean().default(true),
// Validate that ref values (`ref.current`) are not accessed during render.
- validateRefAccessDuringRender: z.boolean().default(false),
+ validateRefAccessDuringRender: z.boolean().default(true),
/*
* Validates that setState is not unconditionally called during render, as it can lead to
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md
deleted file mode 100644
index b7371108d586..000000000000
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md
+++ /dev/null
@@ -1,69 +0,0 @@
-
-## Input
-
-```javascript
-import {useRef} from 'react';
-import {addOne} from 'shared-runtime';
-
-function useKeyCommand() {
- const currentPosition = useRef(0);
- const handleKey = direction => () => {
- const position = currentPosition.current;
- const nextPosition = direction === 'left' ? addOne(position) : position;
- currentPosition.current = nextPosition;
- };
- const moveLeft = {
- handler: handleKey('left'),
- };
- const moveRight = {
- handler: handleKey('right'),
- };
- return [moveLeft, moveRight];
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: useKeyCommand,
- params: [],
-};
-
-```
-
-## Code
-
-```javascript
-import { c as _c } from "react/compiler-runtime";
-import { useRef } from "react";
-import { addOne } from "shared-runtime";
-
-function useKeyCommand() {
- const $ = _c(1);
- const currentPosition = useRef(0);
- let t0;
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
- const handleKey = (direction) => () => {
- const position = currentPosition.current;
- const nextPosition = direction === "left" ? addOne(position) : position;
- currentPosition.current = nextPosition;
- };
-
- const moveLeft = { handler: handleKey("left") };
-
- const moveRight = { handler: handleKey("right") };
-
- t0 = [moveLeft, moveRight];
- $[0] = t0;
- } else {
- t0 = $[0];
- }
- return t0;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: useKeyCommand,
- params: [],
-};
-
-```
-
-### Eval output
-(kind: ok) [{"handler":"[[ function params=0 ]]"},{"handler":"[[ function params=0 ]]"}]
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md
new file mode 100644
index 000000000000..52350036257d
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md
@@ -0,0 +1,50 @@
+
+## Input
+
+```javascript
+import {useRef} from 'react';
+import {addOne} from 'shared-runtime';
+
+function useKeyCommand() {
+ const currentPosition = useRef(0);
+ const handleKey = direction => () => {
+ const position = currentPosition.current;
+ const nextPosition = direction === 'left' ? addOne(position) : position;
+ currentPosition.current = nextPosition;
+ };
+ const moveLeft = {
+ handler: handleKey('left'),
+ };
+ const moveRight = {
+ handler: handleKey('right'),
+ };
+ return [moveLeft, moveRight];
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useKeyCommand,
+ params: [],
+};
+
+```
+
+
+## Error
+
+```
+ 10 | };
+ 11 | const moveLeft = {
+> 12 | handler: handleKey('left'),
+ | ^^^^^^^^^ 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)
+
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12)
+
+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)
+
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15)
+ 13 | };
+ 14 | const moveRight = {
+ 15 | handler: handleKey('right'),
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.tsx
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.tsx
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.tsx
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md
new file mode 100644
index 000000000000..1e5fda2a35fb
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md
@@ -0,0 +1,40 @@
+
+## Input
+
+```javascript
+import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
+
+function Foo(props, ref) {
+ const value = {};
+ if (CONST_TRUE) {
+ mutate(value);
+ return ;
+ }
+ mutate(value);
+ if (CONST_TRUE) {
+ return ;
+ }
+ return value;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Foo,
+ params: [{}, {current: 'fake-ref-object'}],
+};
+
+```
+
+
+## Error
+
+```
+ 9 | mutate(value);
+ 10 | if (CONST_TRUE) {
+> 11 | return ;
+ | ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (11:11)
+ 12 | }
+ 13 | return value;
+ 14 | }
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.tsx
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.tsx
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.tsx
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md
new file mode 100644
index 000000000000..dbc3060a26f4
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md
@@ -0,0 +1,42 @@
+
+## Input
+
+```javascript
+// @enablePreserveExistingMemoizationGuarantees:false
+import {useCallback, useRef} from 'react';
+
+function Component(props) {
+ const ref = useRef({inner: null});
+
+ const onChange = useCallback(event => {
+ // The ref should still be mutable here even though function deps are frozen in
+ // @enablePreserveExistingMemoizationGuarantees mode
+ ref.current.inner = event.target.value;
+ });
+
+ ref.current.inner = null;
+
+ return ;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{}],
+};
+
+```
+
+
+## Error
+
+```
+ 11 | });
+ 12 |
+> 13 | ref.current.inner = null;
+ | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
+ 14 |
+ 15 | return ;
+ 16 | }
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.js
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.js
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.js
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md
deleted file mode 100644
index 54184be0f3f3..000000000000
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md
+++ /dev/null
@@ -1,70 +0,0 @@
-
-## Input
-
-```javascript
-// @enableReactiveScopesInHIR:false
-import {useRef} from 'react';
-import {addOne} from 'shared-runtime';
-
-function useKeyCommand() {
- const currentPosition = useRef(0);
- const handleKey = direction => () => {
- const position = currentPosition.current;
- const nextPosition = direction === 'left' ? addOne(position) : position;
- currentPosition.current = nextPosition;
- };
- const moveLeft = {
- handler: handleKey('left'),
- };
- const moveRight = {
- handler: handleKey('right'),
- };
- return [moveLeft, moveRight];
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: useKeyCommand,
- params: [],
-};
-
-```
-
-## Code
-
-```javascript
-import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false
-import { useRef } from "react";
-import { addOne } from "shared-runtime";
-
-function useKeyCommand() {
- const $ = _c(1);
- const currentPosition = useRef(0);
- let t0;
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
- const handleKey = (direction) => () => {
- const position = currentPosition.current;
- const nextPosition = direction === "left" ? addOne(position) : position;
- currentPosition.current = nextPosition;
- };
-
- const moveLeft = { handler: handleKey("left") };
-
- const moveRight = { handler: handleKey("right") };
-
- t0 = [moveLeft, moveRight];
- $[0] = t0;
- } else {
- t0 = $[0];
- }
- return t0;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: useKeyCommand,
- params: [],
-};
-
-```
-
-### Eval output
-(kind: ok) [{"handler":"[[ function params=0 ]]"},{"handler":"[[ function params=0 ]]"}]
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md
new file mode 100644
index 000000000000..f0b0e6f3a867
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md
@@ -0,0 +1,51 @@
+
+## Input
+
+```javascript
+// @enableReactiveScopesInHIR:false
+import {useRef} from 'react';
+import {addOne} from 'shared-runtime';
+
+function useKeyCommand() {
+ const currentPosition = useRef(0);
+ const handleKey = direction => () => {
+ const position = currentPosition.current;
+ const nextPosition = direction === 'left' ? addOne(position) : position;
+ currentPosition.current = nextPosition;
+ };
+ const moveLeft = {
+ handler: handleKey('left'),
+ };
+ const moveRight = {
+ handler: handleKey('right'),
+ };
+ return [moveLeft, moveRight];
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useKeyCommand,
+ params: [],
+};
+
+```
+
+
+## Error
+
+```
+ 11 | };
+ 12 | const moveLeft = {
+> 13 | handler: handleKey('left'),
+ | ^^^^^^^^^ 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)
+
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13)
+
+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)
+
+InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16)
+ 14 | };
+ 15 | const moveRight = {
+ 16 | handler: handleKey('right'),
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.tsx
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.tsx
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.tsx
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md
new file mode 100644
index 000000000000..1ac3884f92f2
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md
@@ -0,0 +1,35 @@
+
+## Input
+
+```javascript
+// @validatePreserveExistingMemoizationGuarantees:true
+
+import {useRef, useMemo} from 'react';
+import {makeArray} from 'shared-runtime';
+
+function useFoo() {
+ const r = useRef();
+ return useMemo(() => makeArray(r), []);
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: useFoo,
+ params: [],
+};
+
+```
+
+
+## Error
+
+```
+ 6 | function useFoo() {
+ 7 | const r = useRef();
+> 8 | return useMemo(() => makeArray(r), []);
+ | ^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (8:8)
+ 9 | }
+ 10 |
+ 11 | export const FIXTURE_ENTRYPOINT = {
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.ts
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.ts
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.ts
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md
new file mode 100644
index 000000000000..de37e0d3d439
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md
@@ -0,0 +1,31 @@
+
+## Input
+
+```javascript
+// @flow @validatePreserveExistingMemoizationGuarantees
+import {identity} from 'shared-runtime';
+
+component Component(disableLocalRef, ref) {
+ const localRef = useFooRef();
+ const mergedRef = useMemo(() => {
+ return disableLocalRef ? ref : identity(ref, localRef);
+ }, [disableLocalRef, ref, localRef]);
+ return
;
+}
+
+```
+
+
+## Error
+
+```
+ 5 | const localRef = useFooRef();
+ 6 | const mergedRef = useMemo(() => {
+> 7 | return disableLocalRef ? ref : identity(ref, localRef);
+ | ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (7:7)
+ 8 | }, [disableLocalRef, ref, localRef]);
+ 9 | return ;
+ 10 | }
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.js
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.js
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.js
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md
deleted file mode 100644
index 61ab6bafaecd..000000000000
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md
+++ /dev/null
@@ -1,53 +0,0 @@
-
-## Input
-
-```javascript
-// @validatePreserveExistingMemoizationGuarantees:true
-
-import {useRef, useMemo} from 'react';
-import {makeArray} from 'shared-runtime';
-
-function useFoo() {
- const r = useRef();
- return useMemo(() => makeArray(r), []);
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: useFoo,
- params: [],
-};
-
-```
-
-## Code
-
-```javascript
-import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees:true
-
-import { useRef, useMemo } from "react";
-import { makeArray } from "shared-runtime";
-
-function useFoo() {
- const $ = _c(1);
- const r = useRef();
- let t0;
- let t1;
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
- t1 = makeArray(r);
- $[0] = t1;
- } else {
- t1 = $[0];
- }
- t0 = t1;
- return t0;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: useFoo,
- params: [],
-};
-
-```
-
-### Eval output
-(kind: ok) [{}]
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md
deleted file mode 100644
index 1fc118ac59ba..000000000000
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-## Input
-
-```javascript
-// @flow @validatePreserveExistingMemoizationGuarantees
-import {identity} from 'shared-runtime';
-
-component Component(disableLocalRef, ref) {
- const localRef = useFooRef();
- const mergedRef = useMemo(() => {
- return disableLocalRef ? ref : identity(ref, localRef);
- }, [disableLocalRef, ref, localRef]);
- return ;
-}
-
-```
-
-## Code
-
-```javascript
-import { c as _c } from "react/compiler-runtime";
-import { identity } from "shared-runtime";
-
-const Component = React.forwardRef(Component_withRef);
-function Component_withRef(t0, ref) {
- const $ = _c(6);
- const { disableLocalRef } = t0;
- const localRef = useFooRef();
- let t1;
- let t2;
- if ($[0] !== disableLocalRef || $[1] !== ref || $[2] !== localRef) {
- t2 = disableLocalRef ? ref : identity(ref, localRef);
- $[0] = disableLocalRef;
- $[1] = ref;
- $[2] = localRef;
- $[3] = t2;
- } else {
- t2 = $[3];
- }
- t1 = t2;
- const mergedRef = t1;
- let t3;
- if ($[4] !== mergedRef) {
- t3 = ;
- $[4] = mergedRef;
- $[5] = t3;
- } else {
- t3 = $[5];
- }
- return t3;
-}
-
-```
-
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md
deleted file mode 100644
index 8b656b8e23b2..000000000000
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md
+++ /dev/null
@@ -1,89 +0,0 @@
-
-## Input
-
-```javascript
-import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime';
-
-function Foo(props, ref) {
- const value = {};
- if (CONST_TRUE) {
- mutate(value);
- return ;
- }
- mutate(value);
- if (CONST_TRUE) {
- return ;
- }
- return value;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: Foo,
- params: [{}, {current: 'fake-ref-object'}],
-};
-
-```
-
-## Code
-
-```javascript
-import { c as _c } from "react/compiler-runtime";
-import { Stringify, identity, mutate, CONST_TRUE } from "shared-runtime";
-
-function Foo(props, ref) {
- const $ = _c(7);
- let value;
- let t0;
- if ($[0] !== ref) {
- t0 = Symbol.for("react.early_return_sentinel");
- bb0: {
- value = {};
- if (CONST_TRUE) {
- mutate(value);
- t0 = ;
- break bb0;
- }
-
- mutate(value);
- }
- $[0] = ref;
- $[1] = value;
- $[2] = t0;
- } else {
- value = $[1];
- t0 = $[2];
- }
- if (t0 !== Symbol.for("react.early_return_sentinel")) {
- return t0;
- }
- if (CONST_TRUE) {
- let t1;
- if ($[3] !== ref) {
- t1 = identity(ref);
- $[3] = ref;
- $[4] = t1;
- } else {
- t1 = $[4];
- }
- let t2;
- if ($[5] !== t1) {
- t2 = ;
- $[5] = t1;
- $[6] = t2;
- } else {
- t2 = $[6];
- }
- return t2;
- }
- return value;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: Foo,
- params: [{}, { current: "fake-ref-object" }],
-};
-
-```
-
-### Eval output
-(kind: ok) {"ref":{"current":"fake-ref-object"}}
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md
deleted file mode 100644
index 1efca9f0066b..000000000000
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md
+++ /dev/null
@@ -1,75 +0,0 @@
-
-## Input
-
-```javascript
-// @enablePreserveExistingMemoizationGuarantees:false
-import {useCallback, useRef} from 'react';
-
-function Component(props) {
- const ref = useRef({inner: null});
-
- const onChange = useCallback(event => {
- // The ref should still be mutable here even though function deps are frozen in
- // @enablePreserveExistingMemoizationGuarantees mode
- ref.current.inner = event.target.value;
- });
-
- ref.current.inner = null;
-
- return ;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: Component,
- params: [{}],
-};
-
-```
-
-## Code
-
-```javascript
-import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false
-import { useCallback, useRef } from "react";
-
-function Component(props) {
- const $ = _c(3);
- let t0;
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
- t0 = { inner: null };
- $[0] = t0;
- } else {
- t0 = $[0];
- }
- const ref = useRef(t0);
- let t1;
- if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
- t1 = (event) => {
- ref.current.inner = event.target.value;
- };
- $[1] = t1;
- } else {
- t1 = $[1];
- }
- const onChange = t1;
-
- ref.current.inner = null;
- let t2;
- if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
- t2 = ;
- $[2] = t2;
- } else {
- t2 = $[2];
- }
- return t2;
-}
-
-export const FIXTURE_ENTRYPOINT = {
- fn: Component,
- params: [{}],
-};
-
-```
-
-### Eval output
-(kind: ok)
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts
index 8150a523263c..706563b33b45 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts
@@ -14,16 +14,16 @@ describe('parseConfigPragma()', () => {
// Validate defaults first to make sure that the parser is getting the value from the pragma,
// and not just missing it and getting the default value
expect(defaultConfig.enableUseTypeAnnotations).toBe(false);
- expect(defaultConfig.validateRefAccessDuringRender).toBe(false);
+ expect(defaultConfig.validateNoSetStateInPassiveEffects).toBe(false);
expect(defaultConfig.validateNoSetStateInRender).toBe(true);
const config = parseConfigPragma(
- '@enableUseTypeAnnotations @validateRefAccessDuringRender:true @validateNoSetStateInRender:false',
+ '@enableUseTypeAnnotations @validateNoSetStateInPassiveEffects:true @validateNoSetStateInRender:false',
);
expect(config).toEqual({
...defaultConfig,
enableUseTypeAnnotations: true,
- validateRefAccessDuringRender: true,
+ validateNoSetStateInPassiveEffects: true,
validateNoSetStateInRender: false,
});
});
diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts
index c87bcadbe98b..c1aba9f4c840 100644
--- a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts
+++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts
@@ -93,12 +93,12 @@ const tests: CompilerTestCases = {
`,
},
{
- // TODO(gsn): Move this to invalid test suite, when we turn on
- // validateRefAccessDuringRender validation
+ // Don't report the issue if Flow already has
name: '[InvalidInput] Ref access during render',
code: normalizeIndent`
function Component(props) {
const ref = useRef(null);
+ // $FlowFixMe[react-rule-unsafe-ref]
const value = ref.current;
return value;
}
@@ -106,6 +106,22 @@ const tests: CompilerTestCases = {
},
],
invalid: [
+ {
+ name: '[InvalidInput] Ref access during render',
+ code: normalizeIndent`
+ function Component(props) {
+ const ref = useRef(null);
+ const value = ref.current;
+ return value;
+ }
+ `,
+ errors: [
+ {
+ message:
+ 'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)',
+ },
+ ],
+ },
{
name: 'Reportable levels can be configured',
options: [{reportableLevels: new Set([ErrorSeverity.Todo])}],
diff --git a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts
index 2fe34a0b7ff2..7b6525842c4d 100644
--- a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts
+++ b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts
@@ -179,7 +179,10 @@ const rule: Rule.RuleModule = {
if (!isReportableDiagnostic(detail)) {
return;
}
- if (hasFlowSuppression(detail.loc, 'react-rule-hook')) {
+ if (
+ hasFlowSuppression(detail.loc, 'react-rule-hook') ||
+ hasFlowSuppression(detail.loc, 'react-rule-unsafe-ref')
+ ) {
// If Flow already caught this error, we don't need to report it again.
return;
}