@samitouri / QOS-React / commits / 7296120396

[compiler] Update ValidateNoDerivedComputationsInEffects_exp to log the error instead of throwing (#34972)

Summary: TSIA Simple change to log errors in Pipeline.ts instead of throwing in the validation Test Plan: updated snap tests --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34972). * #35044 * #35020 * #34973 * __->__ #34972

Jorge Cabiedes committed Nov 10, 2025 at 12:16 UTC 72961203966a2f1d34dfca089e0a94a94ead7658
44 files changed +893 -592
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+1 -1
@@ -277,7 +277,7 @@ function runWithEnvironment(
277 }
278
279 if (env.config.validateNoDerivedComputationsInEffects_exp) {
280 - validateNoDerivedComputationsInEffects_exp(hir);
280 + env.logErrors(validateNoDerivedComputationsInEffects_exp(hir));
281 }
282
283 if (env.config.validateNoSetStateInEffects) {
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoDerivedComputationsInEffects_exp.ts
+3 -4
@@ -5,6 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 +import {Result} from '../Utils/Result';
9 import {CompilerDiagnostic, CompilerError, Effect} from '..';
10 import {ErrorCategory} from '../CompilerError';
11 import {
@@ -173,7 +174,7 @@ function isNamedIdentifier(place: Place): place is Place & {
174 */
175 export function validateNoDerivedComputationsInEffects_exp(
176 fn: HIRFunction,
176 -): void {
177 +): Result<void, CompilerError> {
178 const functions: Map<IdentifierId, FunctionExpression> = new Map();
179 const derivationCache = new DerivationCache();
180 const errors = new CompilerError();
@@ -236,9 +237,7 @@ export function validateNoDerivedComputationsInEffects_exp(
237 validateEffect(effect, context);
238 }
239
239 - if (errors.hasAnyErrors()) {
240 - throw errors;
241 - }
240 + return errors.asResult();
241 }
242
243 function recordPhiDerivations(
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.expect.md new
+86
@@ -0,0 +1,86 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +function Component({value, enabled}) {
9 + const [localValue, setLocalValue] = useState('');
10 +
11 + useEffect(() => {
12 + if (enabled) {
13 + setLocalValue(value);
14 + } else {
15 + setLocalValue('disabled');
16 + }
17 + }, [value, enabled]);
18 +
19 + return <div>{localValue}</div>;
20 +}
21 +
22 +export const FIXTURE_ENTRYPOINT = {
23 + fn: Component,
24 + params: [{value: 'test', enabled: true}],
25 +};
26 +
27 +```
28 +
29 +## Code
30 +
31 +```javascript
32 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
33 +import { useEffect, useState } from "react";
34 +
35 +function Component(t0) {
36 + const $ = _c(6);
37 + const { value, enabled } = t0;
38 + const [localValue, setLocalValue] = useState("");
39 + let t1;
40 + let t2;
41 + if ($[0] !== enabled || $[1] !== value) {
42 + t1 = () => {
43 + if (enabled) {
44 + setLocalValue(value);
45 + } else {
46 + setLocalValue("disabled");
47 + }
48 + };
49 +
50 + t2 = [value, enabled];
51 + $[0] = enabled;
52 + $[1] = value;
53 + $[2] = t1;
54 + $[3] = t2;
55 + } else {
56 + t1 = $[2];
57 + t2 = $[3];
58 + }
59 + useEffect(t1, t2);
60 + let t3;
61 + if ($[4] !== localValue) {
62 + t3 = <div>{localValue}</div>;
63 + $[4] = localValue;
64 + $[5] = t3;
65 + } else {
66 + t3 = $[5];
67 + }
68 + return t3;
69 +}
70 +
71 +export const FIXTURE_ENTRYPOINT = {
72 + fn: Component,
73 + params: [{ value: "test", enabled: true }],
74 +};
75 +
76 +```
77 +
78 +## Logs
79 +
80 +```
81 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":6,"index":244},"end":{"line":9,"column":19,"index":257},"filename":"derived-state-conditionally-in-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
82 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":16,"column":1,"index":378},"filename":"derived-state-conditionally-in-effect.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
83 +```
84 +
85 +### Eval output
86 +(kind: ok) <div>test</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({value, enabled}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.expect.md new
+78
@@ -0,0 +1,78 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +export default function Component({input = 'empty'}) {
9 + const [currInput, setCurrInput] = useState(input);
10 + const localConst = 'local const';
11 +
12 + useEffect(() => {
13 + setCurrInput(input + localConst);
14 + }, [input, localConst]);
15 +
16 + return <div>{currInput}</div>;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{input: 'test'}],
22 +};
23 +
24 +```
25 +
26 +## Code
27 +
28 +```javascript
29 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
30 +import { useEffect, useState } from "react";
31 +
32 +export default function Component(t0) {
33 + const $ = _c(5);
34 + const { input: t1 } = t0;
35 + const input = t1 === undefined ? "empty" : t1;
36 + const [currInput, setCurrInput] = useState(input);
37 + let t2;
38 + let t3;
39 + if ($[0] !== input) {
40 + t2 = () => {
41 + setCurrInput(input + "local const");
42 + };
43 + t3 = [input, "local const"];
44 + $[0] = input;
45 + $[1] = t2;
46 + $[2] = t3;
47 + } else {
48 + t2 = $[1];
49 + t3 = $[2];
50 + }
51 + useEffect(t2, t3);
52 + let t4;
53 + if ($[3] !== currInput) {
54 + t4 = <div>{currInput}</div>;
55 + $[3] = currInput;
56 + $[4] = t4;
57 + } else {
58 + t4 = $[4];
59 + }
60 + return t4;
61 +}
62 +
63 +export const FIXTURE_ENTRYPOINT = {
64 + fn: Component,
65 + params: [{ input: "test" }],
66 +};
67 +
68 +```
69 +
70 +## Logs
71 +
72 +```
73 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [input]\n\nData Flow Tree:\n└── input (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":276},"end":{"line":9,"column":16,"index":288},"filename":"derived-state-from-default-props.ts","identifierName":"setCurrInput"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
74 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":13,"column":1,"index":372},"filename":"derived-state-from-default-props.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
75 +```
76 +
77 +### Eval output
78 +(kind: ok) <div>testlocal const</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 export default function Component({input = 'empty'}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.expect.md new
+77
@@ -0,0 +1,77 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +
7 +import {useEffect, useState} from 'react';
8 +
9 +function Component({shouldChange}) {
10 + const [count, setCount] = useState(0);
11 +
12 + useEffect(() => {
13 + if (shouldChange) {
14 + setCount(count + 1);
15 + }
16 + }, [count]);
17 +
18 + return <div>{count}</div>;
19 +}
20 +
21 +```
22 +
23 +## Code
24 +
25 +```javascript
26 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
27 +
28 +import { useEffect, useState } from "react";
29 +
30 +function Component(t0) {
31 + const $ = _c(7);
32 + const { shouldChange } = t0;
33 + const [count, setCount] = useState(0);
34 + let t1;
35 + if ($[0] !== count || $[1] !== shouldChange) {
36 + t1 = () => {
37 + if (shouldChange) {
38 + setCount(count + 1);
39 + }
40 + };
41 + $[0] = count;
42 + $[1] = shouldChange;
43 + $[2] = t1;
44 + } else {
45 + t1 = $[2];
46 + }
47 + let t2;
48 + if ($[3] !== count) {
49 + t2 = [count];
50 + $[3] = count;
51 + $[4] = t2;
52 + } else {
53 + t2 = $[4];
54 + }
55 + useEffect(t1, t2);
56 + let t3;
57 + if ($[5] !== count) {
58 + t3 = <div>{count}</div>;
59 + $[5] = count;
60 + $[6] = t3;
61 + } else {
62 + t3 = $[6];
63 + }
64 + return t3;
65 +}
66 +
67 +```
68 +
69 +## Logs
70 +
71 +```
72 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [count]\n\nData Flow Tree:\n└── count (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":6,"index":237},"end":{"line":10,"column":14,"index":245},"filename":"derived-state-from-local-state-in-effect.ts","identifierName":"setCount"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
73 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":108},"end":{"line":15,"column":1,"index":310},"filename":"derived-state-from-local-state-in-effect.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
74 +```
75 +
76 +### Eval output
77 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2
3 import {useEffect, useState} from 'react';
4
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.expect.md new
+115
@@ -0,0 +1,115 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +function Component({firstName}) {
9 + const [lastName, setLastName] = useState('Doe');
10 + const [fullName, setFullName] = useState('John');
11 +
12 + const middleName = 'D.';
13 +
14 + useEffect(() => {
15 + setFullName(firstName + ' ' + middleName + ' ' + lastName);
16 + }, [firstName, middleName, lastName]);
17 +
18 + return (
19 + <div>
20 + <input value={lastName} onChange={e => setLastName(e.target.value)} />
21 + <div>{fullName}</div>
22 + </div>
23 + );
24 +}
25 +
26 +export const FIXTURE_ENTRYPOINT = {
27 + fn: Component,
28 + params: [{firstName: 'John'}],
29 +};
30 +
31 +```
32 +
33 +## Code
34 +
35 +```javascript
36 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
37 +import { useEffect, useState } from "react";
38 +
39 +function Component(t0) {
40 + const $ = _c(12);
41 + const { firstName } = t0;
42 + const [lastName, setLastName] = useState("Doe");
43 + const [fullName, setFullName] = useState("John");
44 + let t1;
45 + let t2;
46 + if ($[0] !== firstName || $[1] !== lastName) {
47 + t1 = () => {
48 + setFullName(firstName + " " + "D." + " " + lastName);
49 + };
50 + t2 = [firstName, "D.", lastName];
51 + $[0] = firstName;
52 + $[1] = lastName;
53 + $[2] = t1;
54 + $[3] = t2;
55 + } else {
56 + t1 = $[2];
57 + t2 = $[3];
58 + }
59 + useEffect(t1, t2);
60 + let t3;
61 + if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
62 + t3 = (e) => setLastName(e.target.value);
63 + $[4] = t3;
64 + } else {
65 + t3 = $[4];
66 + }
67 + let t4;
68 + if ($[5] !== lastName) {
69 + t4 = <input value={lastName} onChange={t3} />;
70 + $[5] = lastName;
71 + $[6] = t4;
72 + } else {
73 + t4 = $[6];
74 + }
75 + let t5;
76 + if ($[7] !== fullName) {
77 + t5 = <div>{fullName}</div>;
78 + $[7] = fullName;
79 + $[8] = t5;
80 + } else {
81 + t5 = $[8];
82 + }
83 + let t6;
84 + if ($[9] !== t4 || $[10] !== t5) {
85 + t6 = (
86 + <div>
87 + {t4}
88 + {t5}
89 + </div>
90 + );
91 + $[9] = t4;
92 + $[10] = t5;
93 + $[11] = t6;
94 + } else {
95 + t6 = $[11];
96 + }
97 + return t6;
98 +}
99 +
100 +export const FIXTURE_ENTRYPOINT = {
101 + fn: Component,
102 + params: [{ firstName: "John" }],
103 +};
104 +
105 +```
106 +
107 +## Logs
108 +
109 +```
110 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [firstName]\nState: [lastName]\n\nData Flow Tree:\n├── firstName (Prop)\n└── lastName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":297},"end":{"line":11,"column":15,"index":308},"filename":"derived-state-from-prop-local-state-and-component-scope.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
111 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":20,"column":1,"index":542},"filename":"derived-state-from-prop-local-state-and-component-scope.ts"},"fnName":"Component","memoSlots":12,"memoBlocks":5,"memoValues":6,"prunedMemoBlocks":0,"prunedMemoValues":0}
112 +```
113 +
114 +### Eval output
115 +(kind: ok) <div><input value="Doe"><div>John D. Doe</div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({firstName}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.expect.md
+8 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 import {useEffect, useState} from 'react';
7
8 function Component({initialName}) {
@@ -29,7 +29,7 @@ export const FIXTURE_ENTRYPOINT = {
29 ## Code
30
31 ```javascript
32 -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
32 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
33 import { useEffect, useState } from "react";
34
35 function Component(t0) {
@@ -79,6 +79,12 @@ export const FIXTURE_ENTRYPOINT = {
79 };
80
81 ```
82 +
83 +## Logs
84 +
85 +```
86 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":16,"column":1,"index":359},"filename":"derived-state-from-prop-setter-call-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0}
87 +```
88
89 ### Eval output
90 (kind: ok) <div><input value="John"></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({initialName}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.expect.md new
+57
@@ -0,0 +1,57 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp
6 +
7 +function Component({value}) {
8 + const [checked, setChecked] = useState('');
9 +
10 + useEffect(() => {
11 + setChecked(value === '' ? [] : value.split(','));
12 + }, [value]);
13 +
14 + return <div>{checked}</div>;
15 +}
16 +
17 +```
18 +
19 +## Code
20 +
21 +```javascript
22 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
23 +
24 +function Component(t0) {
25 + const $ = _c(5);
26 + const { value } = t0;
27 + const [checked, setChecked] = useState("");
28 + let t1;
29 + let t2;
30 + if ($[0] !== value) {
31 + t1 = () => {
32 + setChecked(value === "" ? [] : value.split(","));
33 + };
34 + t2 = [value];
35 + $[0] = value;
36 + $[1] = t1;
37 + $[2] = t2;
38 + } else {
39 + t1 = $[1];
40 + t2 = $[2];
41 + }
42 + useEffect(t1, t2);
43 + let t3;
44 + if ($[3] !== checked) {
45 + t3 = <div>{checked}</div>;
46 + $[3] = checked;
47 + $[4] = t3;
48 + } else {
49 + t3 = $[4];
50 + }
51 + return t3;
52 +}
53 +
54 +```
55 +
56 +### Eval output
57 +(kind: exception) Fixture not implemented
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.js renamed
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.expect.md
+9 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 import {useEffect, useState} from 'react';
7
8 function MockComponent({onSet}) {
@@ -28,7 +28,7 @@ export const FIXTURE_ENTRYPOINT = {
28 ## Code
29
30 ```javascript
31 -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
31 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
32 import { useEffect, useState } from "react";
33
34 function MockComponent(t0) {
@@ -80,6 +80,13 @@ export const FIXTURE_ENTRYPOINT = {
80 };
81
82 ```
83 +
84 +## Logs
85 +
86 +```
87 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":6,"column":1,"index":211},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"MockComponent","memoSlots":2,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
88 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":8,"column":0,"index":213},"end":{"line":15,"column":1,"index":402},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
89 +```
90
91 ### Eval output
92 (kind: ok) <div>Mock Component</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function MockComponent({onSet}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.expect.md new
+78
@@ -0,0 +1,78 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +function Component({value}) {
9 + const [localValue, setLocalValue] = useState('');
10 +
11 + useEffect(() => {
12 + setLocalValue(value);
13 + document.title = `Value: ${value}`;
14 + }, [value]);
15 +
16 + return <div>{localValue}</div>;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{value: 'test'}],
22 +};
23 +
24 +```
25 +
26 +## Code
27 +
28 +```javascript
29 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
30 +import { useEffect, useState } from "react";
31 +
32 +function Component(t0) {
33 + const $ = _c(5);
34 + const { value } = t0;
35 + const [localValue, setLocalValue] = useState("");
36 + let t1;
37 + let t2;
38 + if ($[0] !== value) {
39 + t1 = () => {
40 + setLocalValue(value);
41 + document.title = `Value: ${value}`;
42 + };
43 + t2 = [value];
44 + $[0] = value;
45 + $[1] = t1;
46 + $[2] = t2;
47 + } else {
48 + t1 = $[1];
49 + t2 = $[2];
50 + }
51 + useEffect(t1, t2);
52 + let t3;
53 + if ($[3] !== localValue) {
54 + t3 = <div>{localValue}</div>;
55 + $[3] = localValue;
56 + $[4] = t3;
57 + } else {
58 + t3 = $[4];
59 + }
60 + return t3;
61 +}
62 +
63 +export const FIXTURE_ENTRYPOINT = {
64 + fn: Component,
65 + params: [{ value: "test" }],
66 +};
67 +
68 +```
69 +
70 +## Logs
71 +
72 +```
73 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":8,"column":4,"index":214},"end":{"line":8,"column":17,"index":227},"filename":"derived-state-from-prop-with-side-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
74 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":13,"column":1,"index":327},"filename":"derived-state-from-prop-with-side-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
75 +```
76 +
77 +### Eval output
78 +(kind: ok) <div>test</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({value}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md
+8 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 import {useEffect, useState, useRef} from 'react';
7
8 export default function Component({test}) {
@@ -27,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
27 ## Code
28
29 ```javascript
30 -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
30 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
31 import { useEffect, useState, useRef } from "react";
32
33 export default function Component(t0) {
@@ -68,6 +68,12 @@ export const FIXTURE_ENTRYPOINT = {
68 };
69
70 ```
71 +
72 +## Logs
73 +
74 +```
75 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":130},"end":{"line":14,"column":1,"index":328},"filename":"derived-state-from-ref-and-state-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
76 +```
77
78 ### Eval output
79 (kind: ok) nulltestString
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState, useRef} from 'react';
3
4 export default function Component({test}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.expect.md new
+93
@@ -0,0 +1,93 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +function Component({propValue}) {
9 + const [value, setValue] = useState(null);
10 +
11 + function localFunction() {
12 + console.log('local function');
13 + }
14 +
15 + useEffect(() => {
16 + setValue(propValue);
17 + localFunction();
18 + }, [propValue]);
19 +
20 + return <div>{value}</div>;
21 +}
22 +
23 +export const FIXTURE_ENTRYPOINT = {
24 + fn: Component,
25 + params: [{propValue: 'test'}],
26 +};
27 +
28 +```
29 +
30 +## Code
31 +
32 +```javascript
33 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
34 +import { useEffect, useState } from "react";
35 +
36 +function Component(t0) {
37 + const $ = _c(6);
38 + const { propValue } = t0;
39 + const [value, setValue] = useState(null);
40 + let t1;
41 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
42 + t1 = function localFunction() {
43 + console.log("local function");
44 + };
45 + $[0] = t1;
46 + } else {
47 + t1 = $[0];
48 + }
49 + const localFunction = t1;
50 + let t2;
51 + let t3;
52 + if ($[1] !== propValue) {
53 + t2 = () => {
54 + setValue(propValue);
55 + localFunction();
56 + };
57 + t3 = [propValue];
58 + $[1] = propValue;
59 + $[2] = t2;
60 + $[3] = t3;
61 + } else {
62 + t2 = $[2];
63 + t3 = $[3];
64 + }
65 + useEffect(t2, t3);
66 + let t4;
67 + if ($[4] !== value) {
68 + t4 = <div>{value}</div>;
69 + $[4] = value;
70 + $[5] = t4;
71 + } else {
72 + t4 = $[5];
73 + }
74 + return t4;
75 +}
76 +
77 +export const FIXTURE_ENTRYPOINT = {
78 + fn: Component,
79 + params: [{ propValue: "test" }],
80 +};
81 +
82 +```
83 +
84 +## Logs
85 +
86 +```
87 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [propValue]\n\nData Flow Tree:\n└── propValue (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":12,"column":4,"index":279},"end":{"line":12,"column":12,"index":287},"filename":"effect-contains-local-function-call.ts","identifierName":"setValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
88 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":17,"column":1,"index":371},"filename":"effect-contains-local-function-call.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0}
89 +```
90 +
91 +### Eval output
92 +(kind: ok) <div>test</div>
93 +logs: ['local function']
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({propValue}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md
+9 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 import {useEffect, useState} from 'react';
7
8 function Component({propValue, onChange}) {
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
25 ## Code
26
27 ```javascript
28 -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
28 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
29 import { useEffect, useState } from "react";
30
31 function Component(t0) {
@@ -70,6 +70,13 @@ export const FIXTURE_ENTRYPOINT = {
70 };
71
72 ```
73 +
74 +## Logs
75 +
76 +```
77 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":12,"column":1,"index":306},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
78 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":16,"column":41,"index":402},"end":{"line":16,"column":49,"index":410},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":null,"memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
79 +```
80
81 ### Eval output
82 (kind: ok) <div>test</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({propValue, onChange}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.expect.md
+8 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 import {useEffect, useState} from 'react';
7
8 function Component({propValue}) {
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
25 ## Code
26
27 ```javascript
28 -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
28 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
29 import { useEffect, useState } from "react";
30
31 function Component(t0) {
@@ -65,6 +65,12 @@ export const FIXTURE_ENTRYPOINT = {
65 };
66
67 ```
68 +
69 +## Logs
70 +
71 +```
72 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":12,"column":1,"index":298},"filename":"effect-with-global-function-call-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
73 +```
74
75 ### Eval output
76 (kind: exception) globalCall is not defined
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component({propValue}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.expect.md deleted
-58
@@ -1,58 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -function Component({value, enabled}) {
9 - const [localValue, setLocalValue] = useState('');
10 -
11 - useEffect(() => {
12 - if (enabled) {
13 - setLocalValue(value);
14 - } else {
15 - setLocalValue('disabled');
16 - }
17 - }, [value, enabled]);
18 -
19 - return <div>{localValue}</div>;
20 -}
21 -
22 -export const FIXTURE_ENTRYPOINT = {
23 - fn: Component,
24 - params: [{value: 'test', enabled: true}],
25 -};
26 -
27 -```
28 -
29 -
30 -## Error
31 -
32 -```
33 -Found 1 error:
34 -
35 -Error: You might not need an effect. Derive values in render, not effects.
36 -
37 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
38 -
39 -This setState call is setting a derived value that depends on the following reactive sources:
40 -
41 -Props: [value]
42 -
43 -Data Flow Tree:
44 -└── value (Prop)
45 -
46 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
47 -
48 -error.derived-state-conditionally-in-effect.ts:9:6
49 - 7 | useEffect(() => {
50 - 8 | if (enabled) {
51 -> 9 | setLocalValue(value);
52 - | ^^^^^^^^^^^^^ This should be computed during render, not in an effect
53 - 10 | } else {
54 - 11 | setLocalValue('disabled');
55 - 12 | }
56 -```
57 -
58 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.expect.md deleted
-55
@@ -1,55 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -export default function Component({input = 'empty'}) {
9 - const [currInput, setCurrInput] = useState(input);
10 - const localConst = 'local const';
11 -
12 - useEffect(() => {
13 - setCurrInput(input + localConst);
14 - }, [input, localConst]);
15 -
16 - return <div>{currInput}</div>;
17 -}
18 -
19 -export const FIXTURE_ENTRYPOINT = {
20 - fn: Component,
21 - params: [{input: 'test'}],
22 -};
23 -
24 -```
25 -
26 -
27 -## Error
28 -
29 -```
30 -Found 1 error:
31 -
32 -Error: You might not need an effect. Derive values in render, not effects.
33 -
34 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
35 -
36 -This setState call is setting a derived value that depends on the following reactive sources:
37 -
38 -Props: [input]
39 -
40 -Data Flow Tree:
41 -└── input (Prop)
42 -
43 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
44 -
45 -error.derived-state-from-default-props.ts:9:4
46 - 7 |
47 - 8 | useEffect(() => {
48 -> 9 | setCurrInput(input + localConst);
49 - | ^^^^^^^^^^^^ This should be computed during render, not in an effect
50 - 10 | }, [input, localConst]);
51 - 11 |
52 - 12 | return <div>{currInput}</div>;
53 -```
54 -
55 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-local-state-in-effect.expect.md deleted
-52
@@ -1,52 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -
7 -import {useEffect, useState} from 'react';
8 -
9 -function Component({shouldChange}) {
10 - const [count, setCount] = useState(0);
11 -
12 - useEffect(() => {
13 - if (shouldChange) {
14 - setCount(count + 1);
15 - }
16 - }, [count]);
17 -
18 - return <div>{count}</div>;
19 -}
20 -
21 -```
22 -
23 -
24 -## Error
25 -
26 -```
27 -Found 1 error:
28 -
29 -Error: You might not need an effect. Derive values in render, not effects.
30 -
31 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
32 -
33 -This setState call is setting a derived value that depends on the following reactive sources:
34 -
35 -State: [count]
36 -
37 -Data Flow Tree:
38 -└── count (State)
39 -
40 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
41 -
42 -error.derived-state-from-local-state-in-effect.ts:10:6
43 - 8 | useEffect(() => {
44 - 9 | if (shouldChange) {
45 -> 10 | setCount(count + 1);
46 - | ^^^^^^^^ This should be computed during render, not in an effect
47 - 11 | }
48 - 12 | }, [count]);
49 - 13 |
50 -```
51 -
52 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-local-state-and-component-scope.expect.md deleted
-64
@@ -1,64 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -function Component({firstName}) {
9 - const [lastName, setLastName] = useState('Doe');
10 - const [fullName, setFullName] = useState('John');
11 -
12 - const middleName = 'D.';
13 -
14 - useEffect(() => {
15 - setFullName(firstName + ' ' + middleName + ' ' + lastName);
16 - }, [firstName, middleName, lastName]);
17 -
18 - return (
19 - <div>
20 - <input value={lastName} onChange={e => setLastName(e.target.value)} />
21 - <div>{fullName}</div>
22 - </div>
23 - );
24 -}
25 -
26 -export const FIXTURE_ENTRYPOINT = {
27 - fn: Component,
28 - params: [{firstName: 'John'}],
29 -};
30 -
31 -```
32 -
33 -
34 -## Error
35 -
36 -```
37 -Found 1 error:
38 -
39 -Error: You might not need an effect. Derive values in render, not effects.
40 -
41 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
42 -
43 -This setState call is setting a derived value that depends on the following reactive sources:
44 -
45 -Props: [firstName]
46 -State: [lastName]
47 -
48 -Data Flow Tree:
49 -├── firstName (Prop)
50 -└── lastName (State)
51 -
52 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
53 -
54 -error.derived-state-from-prop-local-state-and-component-scope.ts:11:4
55 - 9 |
56 - 10 | useEffect(() => {
57 -> 11 | setFullName(firstName + ' ' + middleName + ' ' + lastName);
58 - | ^^^^^^^^^^^ This should be computed during render, not in an effect
59 - 12 | }, [firstName, middleName, lastName]);
60 - 13 |
61 - 14 | return (
62 -```
63 -
64 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-setter-ternary.expect.md deleted
-48
@@ -1,48 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -
7 -function Component({value}) {
8 - const [checked, setChecked] = useState('');
9 -
10 - useEffect(() => {
11 - setChecked(value === '' ? [] : value.split(','));
12 - }, [value]);
13 -
14 - return <div>{checked}</div>;
15 -}
16 -
17 -```
18 -
19 -
20 -## Error
21 -
22 -```
23 -Found 1 error:
24 -
25 -Error: You might not need an effect. Derive values in render, not effects.
26 -
27 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
28 -
29 -This setState call is setting a derived value that depends on the following reactive sources:
30 -
31 -Props: [value]
32 -
33 -Data Flow Tree:
34 -└── value (Prop)
35 -
36 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
37 -
38 -error.derived-state-from-prop-setter-ternary.ts:7:4
39 - 5 |
40 - 6 | useEffect(() => {
41 -> 7 | setChecked(value === '' ? [] : value.split(','));
42 - | ^^^^^^^^^^ This should be computed during render, not in an effect
43 - 8 | }, [value]);
44 - 9 |
45 - 10 | return <div>{checked}</div>;
46 -```
47 -
48 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.expect.md deleted
-55
@@ -1,55 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -function Component({value}) {
9 - const [localValue, setLocalValue] = useState('');
10 -
11 - useEffect(() => {
12 - setLocalValue(value);
13 - document.title = `Value: ${value}`;
14 - }, [value]);
15 -
16 - return <div>{localValue}</div>;
17 -}
18 -
19 -export const FIXTURE_ENTRYPOINT = {
20 - fn: Component,
21 - params: [{value: 'test'}],
22 -};
23 -
24 -```
25 -
26 -
27 -## Error
28 -
29 -```
30 -Found 1 error:
31 -
32 -Error: You might not need an effect. Derive values in render, not effects.
33 -
34 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
35 -
36 -This setState call is setting a derived value that depends on the following reactive sources:
37 -
38 -Props: [value]
39 -
40 -Data Flow Tree:
41 -└── value (Prop)
42 -
43 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
44 -
45 -error.derived-state-from-prop-with-side-effect.ts:8:4
46 - 6 |
47 - 7 | useEffect(() => {
48 -> 8 | setLocalValue(value);
49 - | ^^^^^^^^^^^^^ This should be computed during render, not in an effect
50 - 9 | document.title = `Value: ${value}`;
51 - 10 | }, [value]);
52 - 11 |
53 -```
54 -
55 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.effect-contains-local-function-call.expect.md deleted
-59
@@ -1,59 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -function Component({propValue}) {
9 - const [value, setValue] = useState(null);
10 -
11 - function localFunction() {
12 - console.log('local function');
13 - }
14 -
15 - useEffect(() => {
16 - setValue(propValue);
17 - localFunction();
18 - }, [propValue]);
19 -
20 - return <div>{value}</div>;
21 -}
22 -
23 -export const FIXTURE_ENTRYPOINT = {
24 - fn: Component,
25 - params: [{propValue: 'test'}],
26 -};
27 -
28 -```
29 -
30 -
31 -## Error
32 -
33 -```
34 -Found 1 error:
35 -
36 -Error: You might not need an effect. Derive values in render, not effects.
37 -
38 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
39 -
40 -This setState call is setting a derived value that depends on the following reactive sources:
41 -
42 -Props: [propValue]
43 -
44 -Data Flow Tree:
45 -└── propValue (Prop)
46 -
47 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
48 -
49 -error.effect-contains-local-function-call.ts:12:4
50 - 10 |
51 - 11 | useEffect(() => {
52 -> 12 | setValue(propValue);
53 - | ^^^^^^^^ This should be computed during render, not in an effect
54 - 13 | localFunction();
55 - 14 | }, [propValue]);
56 - 15 |
57 -```
58 -
59 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.expect.md deleted
-57
@@ -1,57 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -function Component() {
9 - const [firstName, setFirstName] = useState('Taylor');
10 - const lastName = 'Swift';
11 -
12 - // 🔴 Avoid: redundant state and unnecessary Effect
13 - const [fullName, setFullName] = useState('');
14 - useEffect(() => {
15 - setFullName(firstName + ' ' + lastName);
16 - }, [firstName, lastName]);
17 -
18 - return <div>{fullName}</div>;
19 -}
20 -
21 -export const FIXTURE_ENTRYPOINT = {
22 - fn: Component,
23 - params: [],
24 -};
25 -
26 -```
27 -
28 -
29 -## Error
30 -
31 -```
32 -Found 1 error:
33 -
34 -Error: You might not need an effect. Derive values in render, not effects.
35 -
36 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
37 -
38 -This setState call is setting a derived value that depends on the following reactive sources:
39 -
40 -State: [firstName]
41 -
42 -Data Flow Tree:
43 -└── firstName (State)
44 -
45 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
46 -
47 -error.invalid-derived-computation-in-effect.ts:11:4
48 - 9 | const [fullName, setFullName] = useState('');
49 - 10 | useEffect(() => {
50 -> 11 | setFullName(firstName + ' ' + lastName);
51 - | ^^^^^^^^^^^ This should be computed during render, not in an effect
52 - 12 | }, [firstName, lastName]);
53 - 13 |
54 - 14 | return <div>{fullName}</div>;
55 -```
56 -
57 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.expect.md deleted
-56
@@ -1,56 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -export default function Component(props) {
9 - const [displayValue, setDisplayValue] = useState('');
10 -
11 - useEffect(() => {
12 - const computed = props.prefix + props.value + props.suffix;
13 - setDisplayValue(computed);
14 - }, [props.prefix, props.value, props.suffix]);
15 -
16 - return <div>{displayValue}</div>;
17 -}
18 -
19 -export const FIXTURE_ENTRYPOINT = {
20 - fn: Component,
21 - params: [{prefix: '[', value: 'test', suffix: ']'}],
22 -};
23 -
24 -```
25 -
26 -
27 -## Error
28 -
29 -```
30 -Found 1 error:
31 -
32 -Error: You might not need an effect. Derive values in render, not effects.
33 -
34 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
35 -
36 -This setState call is setting a derived value that depends on the following reactive sources:
37 -
38 -Props: [props]
39 -
40 -Data Flow Tree:
41 -└── computed
42 - └── props (Prop)
43 -
44 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
45 -
46 -error.invalid-derived-state-from-computed-props.ts:9:4
47 - 7 | useEffect(() => {
48 - 8 | const computed = props.prefix + props.value + props.suffix;
49 -> 9 | setDisplayValue(computed);
50 - | ^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
51 - 10 | }, [props.prefix, props.value, props.suffix]);
52 - 11 |
53 - 12 | return <div>{displayValue}</div>;
54 -```
55 -
56 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.expect.md deleted
-56
@@ -1,56 +0,0 @@
1 -
2 -## Input
3 -
4 -```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
6 -import {useEffect, useState} from 'react';
7 -
8 -export default function Component({props}) {
9 - const [fullName, setFullName] = useState(
10 - props.firstName + ' ' + props.lastName
11 - );
12 -
13 - useEffect(() => {
14 - setFullName(props.firstName + ' ' + props.lastName);
15 - }, [props.firstName, props.lastName]);
16 -
17 - return <div>{fullName}</div>;
18 -}
19 -
20 -export const FIXTURE_ENTRYPOINT = {
21 - fn: Component,
22 - params: [{props: {firstName: 'John', lastName: 'Doe'}}],
23 -};
24 -
25 -```
26 -
27 -
28 -## Error
29 -
30 -```
31 -Found 1 error:
32 -
33 -Error: You might not need an effect. Derive values in render, not effects.
34 -
35 -Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user
36 -
37 -This setState call is setting a derived value that depends on the following reactive sources:
38 -
39 -Props: [props]
40 -
41 -Data Flow Tree:
42 -└── props (Prop)
43 -
44 -See: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state.
45 -
46 -error.invalid-derived-state-from-destructured-props.ts:10:4
47 - 8 |
48 - 9 | useEffect(() => {
49 -> 10 | setFullName(props.firstName + ' ' + props.lastName);
50 - | ^^^^^^^^^^^ This should be computed during render, not in an effect
51 - 11 | }, [props.firstName, props.lastName]);
52 - 12 |
53 - 13 | return <div>{fullName}</div>;
54 -```
55 -
56 -
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.expect.md new
+80
@@ -0,0 +1,80 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +function Component() {
9 + const [firstName, setFirstName] = useState('Taylor');
10 + const lastName = 'Swift';
11 +
12 + // 🔴 Avoid: redundant state and unnecessary Effect
13 + const [fullName, setFullName] = useState('');
14 + useEffect(() => {
15 + setFullName(firstName + ' ' + lastName);
16 + }, [firstName, lastName]);
17 +
18 + return <div>{fullName}</div>;
19 +}
20 +
21 +export const FIXTURE_ENTRYPOINT = {
22 + fn: Component,
23 + params: [],
24 +};
25 +
26 +```
27 +
28 +## Code
29 +
30 +```javascript
31 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
32 +import { useEffect, useState } from "react";
33 +
34 +function Component() {
35 + const $ = _c(5);
36 + const [firstName] = useState("Taylor");
37 +
38 + const [fullName, setFullName] = useState("");
39 + let t0;
40 + let t1;
41 + if ($[0] !== firstName) {
42 + t0 = () => {
43 + setFullName(firstName + " " + "Swift");
44 + };
45 + t1 = [firstName, "Swift"];
46 + $[0] = firstName;
47 + $[1] = t0;
48 + $[2] = t1;
49 + } else {
50 + t0 = $[1];
51 + t1 = $[2];
52 + }
53 + useEffect(t0, t1);
54 + let t2;
55 + if ($[3] !== fullName) {
56 + t2 = <div>{fullName}</div>;
57 + $[3] = fullName;
58 + $[4] = t2;
59 + } else {
60 + t2 = $[4];
61 + }
62 + return t2;
63 +}
64 +
65 +export const FIXTURE_ENTRYPOINT = {
66 + fn: Component,
67 + params: [],
68 +};
69 +
70 +```
71 +
72 +## Logs
73 +
74 +```
75 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [firstName]\n\nData Flow Tree:\n└── firstName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":341},"end":{"line":11,"column":15,"index":352},"filename":"invalid-derived-computation-in-effect.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
76 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":15,"column":1,"index":445},"filename":"invalid-derived-computation-in-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
77 +```
78 +
79 +### Eval output
80 +(kind: ok) <div>Taylor Swift</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 function Component() {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.expect.md new
+79
@@ -0,0 +1,79 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +export default function Component(props) {
9 + const [displayValue, setDisplayValue] = useState('');
10 +
11 + useEffect(() => {
12 + const computed = props.prefix + props.value + props.suffix;
13 + setDisplayValue(computed);
14 + }, [props.prefix, props.value, props.suffix]);
15 +
16 + return <div>{displayValue}</div>;
17 +}
18 +
19 +export const FIXTURE_ENTRYPOINT = {
20 + fn: Component,
21 + params: [{prefix: '[', value: 'test', suffix: ']'}],
22 +};
23 +
24 +```
25 +
26 +## Code
27 +
28 +```javascript
29 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
30 +import { useEffect, useState } from "react";
31 +
32 +export default function Component(props) {
33 + const $ = _c(7);
34 + const [displayValue, setDisplayValue] = useState("");
35 + let t0;
36 + let t1;
37 + if ($[0] !== props.prefix || $[1] !== props.suffix || $[2] !== props.value) {
38 + t0 = () => {
39 + const computed = props.prefix + props.value + props.suffix;
40 + setDisplayValue(computed);
41 + };
42 + t1 = [props.prefix, props.value, props.suffix];
43 + $[0] = props.prefix;
44 + $[1] = props.suffix;
45 + $[2] = props.value;
46 + $[3] = t0;
47 + $[4] = t1;
48 + } else {
49 + t0 = $[3];
50 + t1 = $[4];
51 + }
52 + useEffect(t0, t1);
53 + let t2;
54 + if ($[5] !== displayValue) {
55 + t2 = <div>{displayValue}</div>;
56 + $[5] = displayValue;
57 + $[6] = t2;
58 + } else {
59 + t2 = $[6];
60 + }
61 + return t2;
62 +}
63 +
64 +export const FIXTURE_ENTRYPOINT = {
65 + fn: Component,
66 + params: [{ prefix: "[", value: "test", suffix: "]" }],
67 +};
68 +
69 +```
70 +
71 +## Logs
72 +
73 +```
74 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [props]\n\nData Flow Tree:\n└── computed\n └── props (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":295},"end":{"line":9,"column":19,"index":310},"filename":"invalid-derived-state-from-computed-props.ts","identifierName":"setDisplayValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
75 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":13,"column":1,"index":409},"filename":"invalid-derived-state-from-computed-props.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
76 +```
77 +
78 +### Eval output
79 +(kind: ok) <div>[test]</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 export default function Component(props) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.expect.md new
+81
@@ -0,0 +1,81 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 +import {useEffect, useState} from 'react';
7 +
8 +export default function Component({props}) {
9 + const [fullName, setFullName] = useState(
10 + props.firstName + ' ' + props.lastName
11 + );
12 +
13 + useEffect(() => {
14 + setFullName(props.firstName + ' ' + props.lastName);
15 + }, [props.firstName, props.lastName]);
16 +
17 + return <div>{fullName}</div>;
18 +}
19 +
20 +export const FIXTURE_ENTRYPOINT = {
21 + fn: Component,
22 + params: [{props: {firstName: 'John', lastName: 'Doe'}}],
23 +};
24 +
25 +```
26 +
27 +## Code
28 +
29 +```javascript
30 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
31 +import { useEffect, useState } from "react";
32 +
33 +export default function Component(t0) {
34 + const $ = _c(6);
35 + const { props } = t0;
36 + const [fullName, setFullName] = useState(
37 + props.firstName + " " + props.lastName,
38 + );
39 + let t1;
40 + let t2;
41 + if ($[0] !== props.firstName || $[1] !== props.lastName) {
42 + t1 = () => {
43 + setFullName(props.firstName + " " + props.lastName);
44 + };
45 + t2 = [props.firstName, props.lastName];
46 + $[0] = props.firstName;
47 + $[1] = props.lastName;
48 + $[2] = t1;
49 + $[3] = t2;
50 + } else {
51 + t1 = $[2];
52 + t2 = $[3];
53 + }
54 + useEffect(t1, t2);
55 + let t3;
56 + if ($[4] !== fullName) {
57 + t3 = <div>{fullName}</div>;
58 + $[4] = fullName;
59 + $[5] = t3;
60 + } else {
61 + t3 = $[5];
62 + }
63 + return t3;
64 +}
65 +
66 +export const FIXTURE_ENTRYPOINT = {
67 + fn: Component,
68 + params: [{ props: { firstName: "John", lastName: "Doe" } }],
69 +};
70 +
71 +```
72 +
73 +## Logs
74 +
75 +```
76 +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [props]\n\nData Flow Tree:\n└── props (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":4,"index":269},"end":{"line":10,"column":15,"index":280},"filename":"invalid-derived-state-from-destructured-props.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null}
77 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":14,"column":1,"index":397},"filename":"invalid-derived-state-from-destructured-props.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
78 +```
79 +
80 +### Eval output
81 +(kind: ok) <div>John Doe</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.js renamed
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState} from 'react';
3
4 export default function Component({props}) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md
+8 -2
@@ -2,7 +2,7 @@
2 ## Input
3
4 ```javascript
5 -// @validateNoDerivedComputationsInEffects_exp
5 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
6 import {useEffect, useState, useRef} from 'react';
7
8 export default function Component({test}) {
@@ -31,7 +31,7 @@ export const FIXTURE_ENTRYPOINT = {
31 ## Code
32
33 ```javascript
34 -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp
34 +import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
35 import { useEffect, useState, useRef } from "react";
36
37 export default function Component(t0) {
@@ -77,6 +77,12 @@ export const FIXTURE_ENTRYPOINT = {
77 };
78
79 ```
80 +
81 +## Logs
82 +
83 +```
84 +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":130},"end":{"line":18,"column":1,"index":386},"filename":"ref-conditional-in-effect-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0}
85 +```
86
87 ### Eval output
88 (kind: ok) 8
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js
+1 -1
@@ -1,4 +1,4 @@
1 -// @validateNoDerivedComputationsInEffects_exp
1 +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly
2 import {useEffect, useState, useRef} from 'react';
3
4 export default function Component({test}) {