Skip to content

Commit c2326b1

Browse files
authored
[compiler] disallow ref access in state initializer, reducer/initializer (facebook#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). * facebook#34027 * facebook#34026 * __->__ facebook#34025
1 parent 4395689 commit c2326b1

7 files changed

+176
-1
lines changed

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,12 @@ function validateNoRefAccessInRenderImpl(
434434
* By default we check that function call operands are not refs,
435435
* ref values, or functions that can access refs.
436436
*/
437-
if (isRefLValue || hookKind != null) {
437+
if (
438+
isRefLValue ||
439+
(hookKind != null &&
440+
hookKind !== 'useState' &&
441+
hookKind !== 'useReducer')
442+
) {
438443
/**
439444
* Special cases:
440445
*
Original file line numberDiff line numberDiff line change
@@ -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+
Original file line numberDiff line numberDiff line change
@@ -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+
};
Original file line numberDiff line numberDiff line change
@@ -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+
Original file line numberDiff line numberDiff line change
@@ -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+
};
Original file line numberDiff line numberDiff line change
@@ -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+
Original file line numberDiff line numberDiff line change
@@ -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+
};

0 commit comments

Comments
 (0)