@samitouri / QOS-React-2 / commits / 100fc4a8cf

[compiler] Prevent local state source variables from depending on other state (#35044)

Summary: When a local state is created sometimes it uses a `prop` or even other local state for its initial value. This value is only relevant on first render so we shouldn't consider it part of our data flow Test Plan: Added tests

Jorge Cabiedes committed Nov 10, 2025 at 12:29 UTC 100fc4a8cf3b97de53b3566ed726b8ea7e6d7f81
3 files changed +85 -2
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoDerivedComputationsInEffects_exp.ts
+8 -2
@@ -358,8 +358,14 @@ function recordInstructionDerivations(
358 context.effects.add(effectFunction.loweredFunc.func);
359 }
360 } else if (isUseStateType(lvalue.identifier) && value.args.length > 0) {
361 - isSource = true;
362 - typeOfValue = joinValue(typeOfValue, 'fromState');
361 + typeOfValue = 'fromState';
362 + context.derivationCache.addDerivationEntry(
363 + lvalue,
364 + new Set(),
365 + typeOfValue,
366 + true,
367 + );
368 + return;
369 }
370 }
371
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.expect.md new
+59
@@ -0,0 +1,59 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp
6 +
7 +function Component({ prop }) {
8 + const [s, setS] = useState(prop)
9 + const [second, setSecond] = useState(prop)
10 +
11 + useEffect(() => {
12 + setS(second)
13 + }, [second])
14 +
15 + return <div>{s}</div>
16 +}
17 +
18 +```
19 +
20 +## Code
21 +
22 +```javascript
23 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
24 +
25 +function Component(t0) {
26 + const $ = _c(5);
27 + const { prop } = t0;
28 + const [s, setS] = useState(prop);
29 + const [second] = useState(prop);
30 + let t1;
31 + let t2;
32 + if ($[0] !== second) {
33 + t1 = () => {
34 + setS(second);
35 + };
36 + t2 = [second];
37 + $[0] = second;
38 + $[1] = t1;
39 + $[2] = t2;
40 + } else {
41 + t1 = $[1];
42 + t2 = $[2];
43 + }
44 + useEffect(t1, t2);
45 + let t3;
46 + if ($[3] !== s) {
47 + t3 = <div>{s}</div>;
48 + $[3] = s;
49 + $[4] = t3;
50 + } else {
51 + t3 = $[4];
52 + }
53 + return t3;
54 +}
55 +
56 +```
57 +
58 +### Eval output
59 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.js new
+18
@@ -0,0 +1,18 @@
1 +// @validateNoDerivedComputationsInEffects_exp
2 +
3 +function Component({prop}) {
4 + const [s, setS] = useState();
5 + const [second, setSecond] = useState(prop);
6 +
7 + /*
8 + * `second` is a source of state. It will inherit the value of `prop` in
9 + * the first render, but after that it will no longer be updated when
10 + * `prop` changes. So we shouldn't consider `second` as being derived from
11 + * `prop`
12 + */
13 + useEffect(() => {
14 + setS(second);
15 + }, [second]);
16 +
17 + return <div>{s}</div>;
18 +}