;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{input: 'test'}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: You might not need an effect. Derive values in render, not effects.
+
+Derived values (From props: [input]) should be computed during render, rather than in effects. Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user.
+
+error.derived-state-from-default-props.ts:9:4
+ 7 |
+ 8 | useEffect(() => {
+> 9 | setCurrInput(input + localConst);
+ | ^^^^^^^^^^^^ This should be computed during render, not in an effect
+ 10 | }, [input, localConst]);
+ 11 |
+ 12 | 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 0000000000000..1a0f5126e7a0b
--- /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,18 @@
+// @validateNoDerivedComputationsInEffects_exp
+import {useEffect, useState} from 'react';
+
+export default function Component({input = 'empty'}) {
+ const [currInput, setCurrInput] = useState(input);
+ const localConst = 'local const';
+
+ useEffect(() => {
+ setCurrInput(input + localConst);
+ }, [input, localConst]);
+
+ return
;
+}
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: You might not need an effect. Derive values in render, not effects.
+
+Derived values (From local state: [count]) should be computed during render, rather than in effects. Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user.
+
+error.derived-state-from-local-state-in-effect.ts:10:6
+ 8 | useEffect(() => {
+ 9 | if (shouldChange) {
+> 10 | setCount(count + 1);
+ | ^^^^^^^^ This should be computed during render, not in an effect
+ 11 | }
+ 12 | }, [count]);
+ 13 |
+```
+
+
\ 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-local-state-in-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-local-state-in-effect.js
new file mode 100644
index 0000000000000..9568e4900296e
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.derived-state-from-local-state-in-effect.js
@@ -0,0 +1,15 @@
+// @validateNoDerivedComputationsInEffects_exp
+
+import {useEffect, useState} from 'react';
+
+function Component({shouldChange}) {
+ const [count, setCount] = useState(0);
+
+ useEffect(() => {
+ if (shouldChange) {
+ setCount(count + 1);
+ }
+ }, [count]);
+
+ return
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: You might not need an effect. Derive values in render, not effects.
+
+Derived values (From local state: [firstName]) should be computed during render, rather than in effects. Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user.
+
+error.invalid-derived-computation-in-effect.ts:11:4
+ 9 | const [fullName, setFullName] = useState('');
+ 10 | useEffect(() => {
+> 11 | setFullName(firstName + ' ' + lastName);
+ | ^^^^^^^^^^^ This should be computed during render, not in an effect
+ 12 | }, [firstName, lastName]);
+ 13 |
+ 14 | 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-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
new file mode 100644
index 0000000000000..17779a5b4c576
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/error.invalid-derived-computation-in-effect.js
@@ -0,0 +1,20 @@
+// @validateNoDerivedComputationsInEffects_exp
+import {useEffect, useState} from 'react';
+
+function Component() {
+ const [firstName, setFirstName] = useState('Taylor');
+ const lastName = 'Swift';
+
+ // 🔴 Avoid: redundant state and unnecessary Effect
+ const [fullName, setFullName] = useState('');
+ useEffect(() => {
+ setFullName(firstName + ' ' + lastName);
+ }, [firstName, lastName]);
+
+ return
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{prefix: '[', value: 'test', suffix: ']'}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: You might not need an effect. Derive values in render, not effects.
+
+Derived values (From props: [props]) should be computed during render, rather than in effects. Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user.
+
+error.invalid-derived-state-from-computed-props.ts:9:4
+ 7 | useEffect(() => {
+ 8 | const computed = props.prefix + props.value + props.suffix;
+> 9 | setDisplayValue(computed);
+ | ^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
+ 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 0000000000000..24afa944fc566
--- /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_exp
+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
;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Component,
+ params: [{props: {firstName: 'John', lastName: 'Doe'}}],
+};
+
+```
+
+
+## Error
+
+```
+Found 1 error:
+
+Error: You might not need an effect. Derive values in render, not effects.
+
+Derived values (From props: [props]) should be computed during render, rather than in effects. Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user.
+
+error.invalid-derived-state-from-destructured-props.ts:10:4
+ 8 |
+ 9 | useEffect(() => {
+> 10 | setFullName(props.firstName + ' ' + props.lastName);
+ | ^^^^^^^^^^^ This should be computed during render, not in an effect
+ 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 0000000000000..bdfb47a2c6aad
--- /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_exp
+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
;
```
\ No newline at end of file
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/error.invalid-derived-computation-in-effect.js
index d803d3c4a3a1f..0209b47ce39bb 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/error.invalid-derived-computation-in-effect.js
@@ -1,4 +1,6 @@
// @validateNoDerivedComputationsInEffects
+import {useEffect, useState} from 'react';
+
function BadExample() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
@@ -6,7 +8,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/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md
index 8592ae65e4b81..250114fdfb391 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md
@@ -60,29 +60,7 @@ This argument is a function which may reassign or mutate `cache` after render, w
> 22 | // The original issue is that `cache` was not memoized together with the returned
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 23 | // function. This was because neither appears to ever be mutated — the function
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 24 | // is known to mutate `cache` but the function isn't called.
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 25 | //
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 26 | // The fix is to detect cases like this — functions that are mutable but not called -
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 27 | // and ensure that their mutable captures are aliased together into the same scope.
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 28 | const cache = new WeakMap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 29 | return input => {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 30 | let output = cache.get(input);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 31 | if (output == null) {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 32 | output = map(input);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 33 | cache.set(input, output);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-> 34 | }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ …
> 35 | return output;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 36 | };
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md
index e9772e67993f9..45e3d365a8d37 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.expect.md
@@ -64,20 +64,7 @@ error.todo-preserve-memo-deps-mixed-optional-nonoptional-property-chain.ts:7:25
> 8 | return identity({
| ^^^^^^^^^^^^^^^^^^^^^
> 9 | callback: () => {
- | ^^^^^^^^^^^^^^^^^^^^^
-> 10 | // This is a bug in our dependency inference: we stop capturing dependencies
- | ^^^^^^^^^^^^^^^^^^^^^
-> 11 | // after x.a.b?.c. But what this dependency is telling us is that if `x.a.b`
- | ^^^^^^^^^^^^^^^^^^^^^
-> 12 | // was non-nullish, then we can access `.c.d?.e`. Thus we should take the
- | ^^^^^^^^^^^^^^^^^^^^^
-> 13 | // full property chain, exactly as-is with optionals/non-optionals, as a
- | ^^^^^^^^^^^^^^^^^^^^^
-> 14 | // dependency
- | ^^^^^^^^^^^^^^^^^^^^^
-> 15 | return identity(x.a.b?.c.d?.e);
- | ^^^^^^^^^^^^^^^^^^^^^
-> 16 | },
+ …
| ^^^^^^^^^^^^^^^^^^^^^
> 17 | });
| ^^^^^^^^^^^^^^^^^^^^^
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md
index 38d10ee0d1bbc..00af7ec6ad598 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/infer-effect-dependencies/bailout-retry/error.todo-syntax.expect.md
@@ -46,18 +46,7 @@ error.todo-syntax.ts:11:2
> 12 | () => {
| ^^^^^^^^^^^
> 13 | try {
- | ^^^^^^^^^^^
-> 14 | console.log(prop1);
- | ^^^^^^^^^^^
-> 15 | } finally {
- | ^^^^^^^^^^^
-> 16 | console.log('exiting');
- | ^^^^^^^^^^^
-> 17 | }
- | ^^^^^^^^^^^
-> 18 | },
- | ^^^^^^^^^^^
-> 19 | [prop1],
+ …
| ^^^^^^^^^^^
> 20 | AUTODEPS
| ^^^^^^^^^^^
diff --git a/packages/eslint-plugin-react-hooks/CHANGELOG.md b/packages/eslint-plugin-react-hooks/CHANGELOG.md
index 0aba9e00561f7..9f88047193e72 100644
--- a/packages/eslint-plugin-react-hooks/CHANGELOG.md
+++ b/packages/eslint-plugin-react-hooks/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 7.0.1
+
+- Disallowed passing inline `useEffectEvent` values as JSX props to guard against accidental propagation. ([#34820](https://github.com/facebook/react/pull/34820) by [@jf-eirinha](https://github.com/jf-eirinha))
+- Switch to `export =` so eslint-plugin-react-hooks emits correct types for consumers in Node16 ESM projects. ([#34949](https://github.com/facebook/react/pull/34949) by [@karlhorky](https://github.com/karlhorky))
+- Tightened the typing of `configs.flat` so the `configs` export is always defined. ([#34950](https://github.com/facebook/react/pull/34950) by [@poteto](https://github.com/poteto))
+- Fix named import runtime errors. ([#34951](https://github.com/facebook/react/pull/34951), [#34953](https://github.com/facebook/react/pull/34953) by [@karlhorky](https://github.com/karlhorky))
+
## 7.0.0
This release slims down presets to just 2 configurations (`recommended` and `recommended-latest`), and all compiler rules are enabled by default.