diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js
new file mode 100644
index 0000000000..fe06b84de2
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js
@@ -0,0 +1,23 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({initialName}) {
+ const [name, setName] = useState('');
+
+ useEffect(() => {
+ setName(initialName);
+ }, [initialName]);
+
+ return (
+
+ // 🟡 If the is also called outside of the effect, it's still wrong but
+ should be solved by hoisting state
+ setName(e.target.value)} />
+
+ );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{initialName: 'John'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js
new file mode 100644
index 0000000000..c1979819ad
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js
@@ -0,0 +1,16 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({propValue}) {
+ const [value, setValue] = useState(null);
+ useEffect(() => {
+ setValue(propValue);
+ }, [propValue]);
+
+ return ;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{propValue: 'test'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md
new file mode 100644
index 0000000000..1bb5e18626
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md
@@ -0,0 +1,73 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState, useRef} from 'react';
+
+export default function Component({test}) {
+ const [local, setLocal] = useState('');
+
+ const myRef = useRef(null);
+
+ useEffect(() => {
+ setLocal(myRef.current + test);
+ }, [test]);
+
+ return <>{local}>;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{test: 'testString'}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects
+import { useEffect, useState, useRef } from "react";
+
+export default function Component(t0) {
+ const $ = _c(5);
+ const { test } = t0;
+ const [local, setLocal] = useState("");
+
+ const myRef = useRef(null);
+ let t1;
+ let t2;
+ if ($[0] !== test) {
+ t1 = () => {
+ setLocal(myRef.current + test);
+ };
+ t2 = [test];
+ $[0] = test;
+ $[1] = t1;
+ $[2] = t2;
+ } else {
+ t1 = $[1];
+ t2 = $[2];
+ }
+ useEffect(t1, t2);
+ let t3;
+ if ($[3] !== local) {
+ t3 = <>{local}>;
+ $[3] = local;
+ $[4] = t3;
+ } else {
+ t3 = $[4];
+ }
+ return t3;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ test: "testString" }],
+};
+
+```
+
+### Eval output
+(kind: ok) nulltestString
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js
new file mode 100644
index 0000000000..dec6ae6daa
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js
@@ -0,0 +1,19 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState, useRef} from 'react';
+
+export default function Component({test}) {
+ const [local, setLocal] = useState('');
+
+ const myRef = useRef(null);
+
+ useEffect(() => {
+ setLocal(myRef.current + test);
+ }, [test]);
+
+ return <>{local}>;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{test: 'testString'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md
new file mode 100644
index 0000000000..f861d60807
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md
@@ -0,0 +1,75 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({propValue, onChange}) {
+ const [value, setValue] = useState(null);
+ useEffect(() => {
+ setValue(propValue);
+ onChange();
+ }, [propValue]);
+
+ return {value}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{propValue: 'test'}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects
+import { useEffect, useState } from "react";
+
+function Component(t0) {
+ const $ = _c(7);
+ const { propValue, onChange } = t0;
+ const [value, setValue] = useState(null);
+ let t1;
+ if ($[0] !== onChange || $[1] !== propValue) {
+ t1 = () => {
+ setValue(propValue);
+ onChange();
+ };
+ $[0] = onChange;
+ $[1] = propValue;
+ $[2] = t1;
+ } else {
+ t1 = $[2];
+ }
+ let t2;
+ if ($[3] !== propValue) {
+ t2 = [propValue];
+ $[3] = propValue;
+ $[4] = t2;
+ } else {
+ t2 = $[4];
+ }
+ useEffect(t1, t2);
+ let t3;
+ if ($[5] !== value) {
+ t3 = {value}
;
+ $[5] = value;
+ $[6] = t3;
+ } else {
+ t3 = $[6];
+ }
+ return t3;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ propValue: "test" }],
+};
+
+```
+
+### Eval output
+(kind: exception) onChange is not a function
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js
new file mode 100644
index 0000000000..5192feecb1
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js
@@ -0,0 +1,17 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({propValue, onChange}) {
+ const [value, setValue] = useState(null);
+ useEffect(() => {
+ setValue(propValue);
+ onChange();
+ }, [propValue]);
+
+ return {value}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{propValue: 'test'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js
new file mode 100644
index 0000000000..6c10927cc1
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js
@@ -0,0 +1,17 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({propValue}) {
+ const [value, setValue] = useState(null);
+ useEffect(() => {
+ setValue(propValue);
+ globalCall();
+ }, [propValue]);
+
+ return {value}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{propValue: 'test'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.expect.md
new file mode 100644
index 0000000000..21f1bb974d
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.expect.md
@@ -0,0 +1,58 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({value, enabled}) {
+ const [localValue, setLocalValue] = useState('');
+
+ useEffect(() => {
+ if (enabled) {
+ setLocalValue(value);
+ } else {
+ setLocalValue('disabled');
+ }
+ }, [value, enabled]);
+
+ return {localValue}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{value: 'test', enabled: true}],
+};
+
+```
+
+
+## Error
+
+```
+Found 2 errors:
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.derived-state-conditionally-in-effect.ts:9:6
+ 7 | useEffect(() => {
+ 8 | if (enabled) {
+> 9 | setLocalValue(value);
+ | ^^^^^^^^^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 10 | } else {
+ 11 | setLocalValue('disabled');
+ 12 | }
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.derived-state-conditionally-in-effect.ts:11:6
+ 9 | setLocalValue(value);
+ 10 | } else {
+> 11 | setLocalValue('disabled');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 12 | }
+ 13 | }, [value, enabled]);
+ 14 |
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.js
new file mode 100644
index 0000000000..79d83b8925
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-conditionally-in-effect.js
@@ -0,0 +1,21 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({value, enabled}) {
+ const [localValue, setLocalValue] = useState('');
+
+ useEffect(() => {
+ if (enabled) {
+ setLocalValue(value);
+ } else {
+ setLocalValue('disabled');
+ }
+ }, [value, enabled]);
+
+ return {localValue}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{value: 'test', enabled: true}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.expect.md
new file mode 100644
index 0000000000..964aae40dc
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.expect.md
@@ -0,0 +1,37 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+
+export default function Component(input = 'empty') {
+ const [currInput, setCurrInput] = useState(input);
+
+ useEffect(() => {
+ setCurrInput(input);
+ }, [input]);
+
+ return {currInput}
;
+}
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.derived-state-from-default-props.ts:7:4
+ 5 |
+ 6 | useEffect(() => {
+> 7 | setCurrInput(input);
+ | ^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 8 | }, [input]);
+ 9 |
+ 10 | return {currInput}
;
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.js
new file mode 100644
index 0000000000..c1ea591316
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-default-props.js
@@ -0,0 +1,11 @@
+// @validateNoDerivedComputationsInEffects
+
+export default function Component(input = 'empty') {
+ const [currInput, setCurrInput] = useState(input);
+
+ useEffect(() => {
+ setCurrInput(input);
+ }, [input]);
+
+ return {currInput}
;
+}
diff --git a/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 b/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
new file mode 100644
index 0000000000..1c994a57ee
--- /dev/null
+++ b/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
@@ -0,0 +1,51 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({firstName}) {
+ const [lastName, setLastName] = useState(null);
+ const [fullName, setFullName] = useState(null);
+
+ const middleName = 'D.';
+
+ useEffect(() => {
+ setFullName(firstName + ' ' + middleName + ' ' + lastName);
+ }, [firstName, middleName, lastName]);
+
+ return (
+
+
setLastName(e.target.value)} />
+
{fullName}
+
+ );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{firstName: 'John'}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.derived-state-from-prop-local-state-and-component-scope.ts:11:4
+ 9 |
+ 10 | useEffect(() => {
+> 11 | setFullName(firstName + ' ' + middleName + ' ' + lastName);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 12 | }, [firstName, middleName, lastName]);
+ 13 |
+ 14 | return (
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-local-state-and-component-scope.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-local-state-and-component-scope.js
new file mode 100644
index 0000000000..4b725d5375
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-local-state-and-component-scope.js
@@ -0,0 +1,25 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({firstName}) {
+ const [lastName, setLastName] = useState(null);
+ const [fullName, setFullName] = useState(null);
+
+ const middleName = 'D.';
+
+ useEffect(() => {
+ setFullName(firstName + ' ' + middleName + ' ' + lastName);
+ }, [firstName, middleName, lastName]);
+
+ return (
+
+
setLastName(e.target.value)} />
+
{fullName}
+
+ );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{firstName: 'John'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.expect.md
new file mode 100644
index 0000000000..fc06bdeb37
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.expect.md
@@ -0,0 +1,43 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+
+function Component({value}) {
+ const [localValue, setLocalValue] = useState('');
+
+ useEffect(() => {
+ setLocalValue(value);
+ document.title = `Value: ${value}`;
+ }, [value]);
+
+ return {localValue}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{value: 'test'}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.derived-state-from-prop-with-side-effect.ts:7:4
+ 5 |
+ 6 | useEffect(() => {
+> 7 | setLocalValue(value);
+ | ^^^^^^^^^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 8 | document.title = `Value: ${value}`;
+ 9 | }, [value]);
+ 10 |
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.js
new file mode 100644
index 0000000000..11c4340eab
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-prop-with-side-effect.js
@@ -0,0 +1,17 @@
+// @validateNoDerivedComputationsInEffects
+
+function Component({value}) {
+ const [localValue, setLocalValue] = useState('');
+
+ useEffect(() => {
+ setLocalValue(value);
+ document.title = `Value: ${value}`;
+ }, [value]);
+
+ return {localValue}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{value: 'test'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.effect-contains-local-function-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.effect-contains-local-function-call.js
new file mode 100644
index 0000000000..00e658d896
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.effect-contains-local-function-call.js
@@ -0,0 +1,22 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+function Component({propValue}) {
+ const [value, setValue] = useState(null);
+
+ function localFunction() {
+ console.log('local function');
+ }
+
+ useEffect(() => {
+ setValue(propValue);
+ localFunction();
+ }, [propValue]);
+
+ return {value}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{propValue: 'test'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-derived-computation-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.expect.md
similarity index 100%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-derived-computation-in-effect.expect.md
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.expect.md
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-derived-computation-in-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.js
similarity index 86%
rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-derived-computation-in-effect.js
rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.js
index d803d3c4a3..63d18c9c17 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-derived-computation-in-effect.js
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.js
@@ -6,7 +6,7 @@ function BadExample() {
// 🔴 Avoid: redundant state and unnecessary Effect
const [fullName, setFullName] = useState('');
useEffect(() => {
- setFullName(capitalize(firstName + ' ' + lastName));
+ setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
return {fullName}
;
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.expect.md
new file mode 100644
index 0000000000..39d71798aa
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.expect.md
@@ -0,0 +1,44 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+export default function Component(props) {
+ const [displayValue, setDisplayValue] = useState('');
+
+ useEffect(() => {
+ const computed = props.prefix + props.value + props.suffix;
+ setDisplayValue(computed);
+ }, [props.prefix, props.value, props.suffix]);
+
+ return {displayValue}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{prefix: '[', value: 'test', suffix: ']'}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.invalid-derived-state-from-computed-props.ts:9:4
+ 7 | useEffect(() => {
+ 8 | const computed = props.prefix + props.value + props.suffix;
+> 9 | setDisplayValue(computed);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 10 | }, [props.prefix, props.value, props.suffix]);
+ 11 |
+ 12 | return {displayValue}
;
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.js
new file mode 100644
index 0000000000..31fb30cef9
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-computed-props.js
@@ -0,0 +1,18 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+export default function Component(props) {
+ const [displayValue, setDisplayValue] = useState('');
+
+ useEffect(() => {
+ const computed = props.prefix + props.value + props.suffix;
+ setDisplayValue(computed);
+ }, [props.prefix, props.value, props.suffix]);
+
+ return {displayValue}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{prefix: '[', value: 'test', suffix: ']'}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.expect.md
new file mode 100644
index 0000000000..7f3f807568
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.expect.md
@@ -0,0 +1,45 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+export default function Component({props}) {
+ const [fullName, setFullName] = useState(
+ props.firstName + ' ' + props.lastName
+ );
+
+ useEffect(() => {
+ setFullName(props.firstName + ' ' + props.lastName);
+ }, [props.firstName, props.lastName]);
+
+ return {fullName}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{props: {firstName: 'John', lastName: 'Doe'}}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+
+error.invalid-derived-state-from-destructured-props.ts:10:4
+ 8 |
+ 9 | useEffect(() => {
+> 10 | setFullName(props.firstName + ' ' + props.lastName);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)
+ 11 | }, [props.firstName, props.lastName]);
+ 12 |
+ 13 | return {fullName}
;
+```
+
+
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.js
new file mode 100644
index 0000000000..2df3df1d52
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-state-from-destructured-props.js
@@ -0,0 +1,19 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
+export default function Component({props}) {
+ const [fullName, setFullName] = useState(
+ props.firstName + ' ' + props.lastName
+ );
+
+ useEffect(() => {
+ setFullName(props.firstName + ' ' + props.lastName);
+ }, [props.firstName, props.lastName]);
+
+ return {fullName}
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{props: {firstName: 'John', lastName: 'Doe'}}],
+};
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md
new file mode 100644
index 0000000000..85050d2459
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md
@@ -0,0 +1,82 @@
+
+## Input
+
+```javascript
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState, useRef} from 'react';
+
+export default function Component({test}) {
+ const [local, setLocal] = useState('');
+
+ const myRef = useRef(null);
+
+ useEffect(() => {
+ if (myRef.current) {
+ setLocal(test + 'Available');
+ } else {
+ setLocal(test + 'NotAvailable');
+ }
+ }, [test]);
+
+ return <>{local}>;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{test: 'testString'}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects
+import { useEffect, useState, useRef } from "react";
+
+export default function Component(t0) {
+ const $ = _c(5);
+ const { test } = t0;
+ const [local, setLocal] = useState("");
+
+ const myRef = useRef(null);
+ let t1;
+ let t2;
+ if ($[0] !== test) {
+ t1 = () => {
+ if (myRef.current) {
+ setLocal(test + "Available");
+ } else {
+ setLocal(test + "NotAvailable");
+ }
+ };
+
+ t2 = [test];
+ $[0] = test;
+ $[1] = t1;
+ $[2] = t2;
+ } else {
+ t1 = $[1];
+ t2 = $[2];
+ }
+ useEffect(t1, t2);
+ let t3;
+ if ($[3] !== local) {
+ t3 = <>{local}>;
+ $[3] = local;
+ $[4] = t3;
+ } else {
+ t3 = $[4];
+ }
+ return t3;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{ test: "testString" }],
+};
+
+```
+
+### Eval output
+(kind: ok) testStringNotAvailable
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js
new file mode 100644
index 0000000000..072490988a
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js
@@ -0,0 +1,23 @@
+// @validateNoDerivedComputationsInEffects
+import {useEffect, useState, useRef} from 'react';
+
+export default function Component({test}) {
+ const [local, setLocal] = useState('');
+
+ const myRef = useRef(null);
+
+ useEffect(() => {
+ if (myRef.current) {
+ setLocal(test + 'Available');
+ } else {
+ setLocal(test + 'NotAvailable');
+ }
+ }, [test]);
+
+ return <>{local}>;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{test: 'testString'}],
+};