@samitouri / QOS-React-2 / commits / c2326b1336

[compiler] disallow ref access in state initializer, reducer/initializer (#34025)

Per title, disallow ref access in `useState()` initializer function, `useReducer()` reducer, and `useReducer()` init function. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34025). * #34027 * #34026 * __->__ #34025

Joseph Savona committed Jul 29, 2025 at 10:56 UTC c2326b1336e38a52899fad6c2ddbb71ea7ddd3ee
7 files changed +176 -1
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts
+6 -1
@@ -434,7 +434,12 @@ function validateNoRefAccessInRenderImpl(
434 * By default we check that function call operands are not refs,
435 * ref values, or functions that can access refs.
436 */
437 - if (isRefLValue || hookKind != null) {
437 + if (
438 + isRefLValue ||
439 + (hookKind != null &&
440 + hookKind !== 'useState' &&
441 + hookKind !== 'useReducer')
442 + ) {
443 /**
444 * Special cases:
445 *
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-in-reducer-init.expect.md new
+45
@@ -0,0 +1,45 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import {useReducer, useRef} from 'react';
6 +
7 +function Component(props) {
8 + const ref = useRef(props.value);
9 + const [state] = useReducer(
10 + (state, action) => state + action,
11 + 0,
12 + init => ref.current
13 + );
14 +
15 + return <Stringify state={state} />;
16 +}
17 +
18 +export const FIXTURE_ENTRYPOINT = {
19 + fn: Component,
20 + params: [{value: 42}],
21 +};
22 +
23 +```
24 +
25 +
26 +## Error
27 +
28 +```
29 +Found 1 error:
30 +
31 +Error: Cannot access refs during render
32 +
33 +React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
34 +
35 +error.invalid-access-ref-in-reducer-init.ts:8:4
36 + 6 | (state, action) => state + action,
37 + 7 | 0,
38 +> 8 | init => ref.current
39 + | ^^^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
40 + 9 | );
41 + 10 |
42 + 11 | return <Stringify state={state} />;
43 +```
44 +
45 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-in-reducer-init.js new
+17
@@ -0,0 +1,17 @@
1 +import {useReducer, useRef} from 'react';
2 +
3 +function Component(props) {
4 + const ref = useRef(props.value);
5 + const [state] = useReducer(
6 + (state, action) => state + action,
7 + 0,
8 + init => ref.current
9 + );
10 +
11 + return <Stringify state={state} />;
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{value: 42}],
17 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-in-reducer.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import {useReducer, useRef} from 'react';
6 +
7 +function Component(props) {
8 + const ref = useRef(props.value);
9 + const [state] = useReducer(() => ref.current, null);
10 +
11 + return <Stringify state={state} />;
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{value: 42}],
17 +};
18 +
19 +```
20 +
21 +
22 +## Error
23 +
24 +```
25 +Found 1 error:
26 +
27 +Error: Cannot access refs during render
28 +
29 +React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
30 +
31 +error.invalid-access-ref-in-reducer.ts:5:29
32 + 3 | function Component(props) {
33 + 4 | const ref = useRef(props.value);
34 +> 5 | const [state] = useReducer(() => ref.current, null);
35 + | ^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
36 + 6 |
37 + 7 | return <Stringify state={state} />;
38 + 8 | }
39 +```
40 +
41 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-in-reducer.js new
+13
@@ -0,0 +1,13 @@
1 +import {useReducer, useRef} from 'react';
2 +
3 +function Component(props) {
4 + const ref = useRef(props.value);
5 + const [state] = useReducer(() => ref.current, null);
6 +
7 + return <Stringify state={state} />;
8 +}
9 +
10 +export const FIXTURE_ENTRYPOINT = {
11 + fn: Component,
12 + params: [{value: 42}],
13 +};
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-in-state-initializer.expect.md new
+41
@@ -0,0 +1,41 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import {useRef, useState} from 'react';
6 +
7 +function Component(props) {
8 + const ref = useRef(props.value);
9 + const [state] = useState(() => ref.current);
10 +
11 + return <Stringify state={state} />;
12 +}
13 +
14 +export const FIXTURE_ENTRYPOINT = {
15 + fn: Component,
16 + params: [{value: 42}],
17 +};
18 +
19 +```
20 +
21 +
22 +## Error
23 +
24 +```
25 +Found 1 error:
26 +
27 +Error: Cannot access refs during render
28 +
29 +React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
30 +
31 +error.invalid-access-ref-in-state-initializer.ts:5:27
32 + 3 | function Component(props) {
33 + 4 | const ref = useRef(props.value);
34 +> 5 | const [state] = useState(() => ref.current);
35 + | ^^^^^^^^^^^^^^^^^ Passing a ref to a function may read its value during render
36 + 6 |
37 + 7 | return <Stringify state={state} />;
38 + 8 | }
39 +```
40 +
41 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-access-ref-in-state-initializer.js new
+13
@@ -0,0 +1,13 @@
1 +import {useRef, useState} from 'react';
2 +
3 +function Component(props) {
4 + const ref = useRef(props.value);
5 + const [state] = useState(() => ref.current);
6 +
7 + return <Stringify state={state} />;
8 +}
9 +
10 +export const FIXTURE_ENTRYPOINT = {
11 + fn: Component,
12 + params: [{value: 42}],
13 +};