Skip to content

Commit 071613b

Browse files
committed
[compiler] Allow nonreactive stable types as extraneous deps
When checking ValidateExhaustiveDeps internally, this seems to be the most common case that it flags. The current exhaustive-deps rule allows extraneous deps if they are a set of stable types. So here we reuse our existing isStableType() util in the compiler to allow this case.
1 parent 12d3951 commit 071613b

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,20 @@ export function validateExhaustiveDependencies(
261261
extra.push(dep);
262262
}
263263

264+
/*
265+
* For compatiblity with the existing exhaustive-deps rule, we allow
266+
* known-stable values as dependencies even if the value is not reactive.
267+
* This allows code that takes a dep on a non-reactive setState function
268+
* to pass, for example.
269+
*/
270+
retainWhere(extra, dep => {
271+
const isNonReactiveStableValue =
272+
dep.root.kind === 'NamedLocal' &&
273+
!dep.root.value.reactive &&
274+
isStableType(dep.root.value.identifier);
275+
return !isNonReactiveStableValue;
276+
});
277+
264278
if (missing.length !== 0 || extra.length !== 0) {
265279
let suggestions: Array<CompilerSuggestion> | null = null;
266280
if (startMemo.depsLoc != null && typeof startMemo.depsLoc !== 'symbol') {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
2+
## Input
3+
4+
```javascript
5+
// @validateExhaustiveMemoizationDependencies
6+
import {
7+
useCallback,
8+
useTransition,
9+
useState,
10+
useOptimistic,
11+
useActionState,
12+
useRef,
13+
useReducer,
14+
} from 'react';
15+
16+
function useFoo() {
17+
const [s, setState] = useState();
18+
const ref = useRef(null);
19+
const [t, startTransition] = useTransition();
20+
const [u, addOptimistic] = useOptimistic();
21+
const [v, dispatch] = useReducer(() => {}, null);
22+
const [isPending, dispatchAction] = useActionState(() => {}, null);
23+
24+
return useCallback(() => {
25+
dispatch();
26+
startTransition(() => {});
27+
addOptimistic();
28+
setState(null);
29+
dispatchAction();
30+
ref.current = true;
31+
}, [
32+
// intentionally adding unnecessary deps on nonreactive stable values
33+
// to check that they're allowed
34+
dispatch,
35+
startTransition,
36+
addOptimistic,
37+
setState,
38+
dispatchAction,
39+
ref,
40+
]);
41+
}
42+
43+
export const FIXTURE_ENTRYPOINT = {
44+
fn: useFoo,
45+
params: [],
46+
};
47+
48+
```
49+
50+
## Code
51+
52+
```javascript
53+
import { c as _c } from "react/compiler-runtime"; // @validateExhaustiveMemoizationDependencies
54+
import {
55+
useCallback,
56+
useTransition,
57+
useState,
58+
useOptimistic,
59+
useActionState,
60+
useRef,
61+
useReducer,
62+
} from "react";
63+
64+
function useFoo() {
65+
const $ = _c(1);
66+
const [, setState] = useState();
67+
const ref = useRef(null);
68+
const [, startTransition] = useTransition();
69+
const [, addOptimistic] = useOptimistic();
70+
const [, dispatch] = useReducer(_temp, null);
71+
const [, dispatchAction] = useActionState(_temp2, null);
72+
let t0;
73+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
74+
t0 = () => {
75+
dispatch();
76+
startTransition(_temp3);
77+
addOptimistic();
78+
setState(null);
79+
dispatchAction();
80+
ref.current = true;
81+
};
82+
$[0] = t0;
83+
} else {
84+
t0 = $[0];
85+
}
86+
return t0;
87+
}
88+
function _temp3() {}
89+
function _temp2() {}
90+
function _temp() {}
91+
92+
export const FIXTURE_ENTRYPOINT = {
93+
fn: useFoo,
94+
params: [],
95+
};
96+
97+
```
98+
99+
### Eval output
100+
(kind: ok) "[[ function params=0 ]]"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// @validateExhaustiveMemoizationDependencies
2+
import {
3+
useCallback,
4+
useTransition,
5+
useState,
6+
useOptimistic,
7+
useActionState,
8+
useRef,
9+
useReducer,
10+
} from 'react';
11+
12+
function useFoo() {
13+
const [s, setState] = useState();
14+
const ref = useRef(null);
15+
const [t, startTransition] = useTransition();
16+
const [u, addOptimistic] = useOptimistic();
17+
const [v, dispatch] = useReducer(() => {}, null);
18+
const [isPending, dispatchAction] = useActionState(() => {}, null);
19+
20+
return useCallback(() => {
21+
dispatch();
22+
startTransition(() => {});
23+
addOptimistic();
24+
setState(null);
25+
dispatchAction();
26+
ref.current = true;
27+
}, [
28+
// intentionally adding unnecessary deps on nonreactive stable values
29+
// to check that they're allowed
30+
dispatch,
31+
startTransition,
32+
addOptimistic,
33+
setState,
34+
dispatchAction,
35+
ref,
36+
]);
37+
}
38+
39+
export const FIXTURE_ENTRYPOINT = {
40+
fn: useFoo,
41+
params: [],
42+
};

0 commit comments

Comments
 (0)