Compare commits

..

2 Commits

Author SHA1 Message Date
Jorge Cabiedes Acosta
2d26ba5483 [compiler] Change ValidateNoDerivedComputationsInEffect logic to track prop and local state derived values variables 2025-09-23 11:25:43 -07:00
Jorge Cabiedes Acosta
f989af23f9 [compiler] ValidateNoDerivedComputationsInEffects test cases
Summary:
This creates the test cases we expect this first iteration of calculate in render to catch

Test Plan:
Test cases:
2025-09-23 11:25:43 -07:00
25 changed files with 243 additions and 284 deletions

View File

@@ -18,9 +18,7 @@ import {
CallExpression,
Instruction,
isUseStateType,
isUseRefType,
} from '../HIR';
import {printInstruction} from '../HIR/PrintHIR';
import {eachInstructionLValue, eachInstructionOperand} from '../HIR/visitors';
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
import {assertExhaustive} from '../Utils/utils';
@@ -277,7 +275,6 @@ function validateEffect(
sourceIds: Set<IdentifierId>;
}> = [];
const globals: Set<IdentifierId> = new Set();
for (const block of effectFunction.body.blocks.values()) {
for (const pred of block.preds) {
if (!seenBlocks.has(pred)) {
@@ -287,11 +284,6 @@ function validateEffect(
}
for (const instr of block.instructions) {
// Early return if any instruction is deriving a value from a ref
if (isUseRefType(instr.lvalue.identifier)) {
return;
}
if (
instr.value.kind === 'CallExpression' &&
isSetStateType(instr.value.callee.identifier) &&
@@ -308,29 +300,6 @@ function validateEffect(
sourceIds: argMetadata.sourcesIds,
});
}
} else if (instr.value.kind === 'CallExpression') {
const calleeMetadata = derivationCache.get(
instr.value.callee.identifier.id,
);
if (
calleeMetadata !== undefined &&
(calleeMetadata.typeOfValue === 'fromProps' ||
calleeMetadata.typeOfValue === 'fromPropsAndState')
) {
// If the callee is a prop we can't confidently say that it should be derived in render
return;
}
if (globals.has(instr.value.callee.identifier.id)) {
// If the callee is a global we can't confidently say that it should be derived in render
return;
}
} else if (instr.value.kind === 'LoadGlobal') {
globals.add(instr.lvalue.identifier.id);
for (const operand of eachInstructionOperand(instr)) {
globals.add(operand.identifier.id);
}
}
}
seenBlocks.add(block.id);
@@ -342,7 +311,7 @@ function validateEffect(
reason:
'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)',
description: null,
loc: derivedSetStateCall.value.callee.loc,
loc: derivedSetStateCall.value.loc,
suggestions: null,
});
}

View File

@@ -0,0 +1,88 @@
## Input
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component({initialName}) {
const [name, setName] = useState('');
useEffect(() => {
setName(initialName);
}, [initialName]);
return (
<div>
// 🟡 If the is also called outside of the effect, it's still wrong but
should be solved by hoisting state
<input value={name} onChange={e => setName(e.target.value)} />
</div>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{initialName: 'John'}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects
import { useEffect, useState } from "react";
function Component(t0) {
const $ = _c(6);
const { initialName } = t0;
const [name, setName] = useState("");
let t1;
let t2;
if ($[0] !== initialName) {
t1 = () => {
setName(initialName);
};
t2 = [initialName];
$[0] = initialName;
$[1] = t1;
$[2] = t2;
} else {
t1 = $[1];
t2 = $[2];
}
useEffect(t1, t2);
let t3;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t3 = (e) => setName(e.target.value);
$[3] = t3;
} else {
t3 = $[3];
}
let t4;
if ($[4] !== name) {
t4 = (
<div>
// 🟡 If the is also called outside of the effect, it's still wrong but
should be solved by hoisting state
<input value={name} onChange={t3} />
</div>
);
$[4] = name;
$[5] = t4;
} else {
t4 = $[5];
}
return t4;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ initialName: "John" }],
};
```
### Eval output
(kind: ok) <div>// 🟡 If the is also called outside of the effect, it's still wrong but should be solved by hoisting state<input value="John"></div>

View File

@@ -10,6 +10,8 @@ function Component({initialName}) {
return (
<div>
// 🟡 If the is also called outside of the effect, it's still wrong but
should be solved by hoisting state
<input value={name} onChange={e => setName(e.target.value)} />
</div>
);

View File

@@ -0,0 +1,67 @@
## Input
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component({propValue}) {
const [value, setValue] = useState(null);
useEffect(() => {
setValue(propValue);
}, [propValue]);
return <MockComponent setter={setValue} />;
}
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(4);
const { propValue } = t0;
const [, setValue] = useState(null);
let t1;
let t2;
if ($[0] !== propValue) {
t1 = () => {
setValue(propValue);
};
t2 = [propValue];
$[0] = propValue;
$[1] = t1;
$[2] = t2;
} else {
t1 = $[1];
t2 = $[2];
}
useEffect(t1, t2);
let t3;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t3 = <MockComponent setter={setValue} />;
$[3] = t3;
} else {
t3 = $[3];
}
return t3;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ propValue: "test" }],
};
```
### Eval output
(kind: exception) MockComponent is not defined

View File

@@ -1,17 +1,13 @@
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function MockComponent({onSet}) {
return <div onClick={() => onSet('clicked')}>Mock Component</div>;
}
function Component({propValue}) {
const [value, setValue] = useState(null);
useEffect(() => {
setValue(propValue);
}, [propValue]);
return <MockComponent onSet={setValue} />;
return <MockComponent setter={setValue} />;
}
export const FIXTURE_ENTRYPOINT = {

View File

@@ -17,7 +17,7 @@ function Component({propValue, onChange}) {
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{propValue: 'test', onChange: () => {}}],
params: [{propValue: 'test'}],
};
```
@@ -66,10 +66,10 @@ function Component(t0) {
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ propValue: "test", onChange: () => {} }],
params: [{ propValue: "test" }],
};
```
### Eval output
(kind: ok) <div>test</div>
(kind: exception) onChange is not a function

View File

@@ -13,5 +13,5 @@ function Component({propValue, onChange}) {
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{propValue: 'test', onChange: () => {}}],
params: [{propValue: 'test'}],
};

View File

@@ -32,13 +32,15 @@ export const FIXTURE_ENTRYPOINT = {
```
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: You might not need an effect. Derive values in render, not effects.
Derived values (From props: [value]) 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-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)
| ^^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
10 | } else {
11 | setLocalValue('disabled');
12 | }

View File

@@ -3,24 +3,17 @@
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
export default function Component({input = 'empty'}) {
export default function Component(input = 'empty') {
const [currInput, setCurrInput] = useState(input);
const localConst = 'local const';
useEffect(() => {
setCurrInput(input + localConst);
}, [input, localConst]);
setCurrInput(input);
}, [input]);
return <div>{currInput}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{input: 'test'}],
};
```
@@ -29,16 +22,18 @@ export const FIXTURE_ENTRYPOINT = {
```
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: You might not need an effect. Derive values in render, not effects.
error.derived-state-from-default-props.ts:9:4
7 |
8 | useEffect(() => {
> 9 | setCurrInput(input + localConst);
| ^^^^^^^^^^^^ 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 | }, [input, localConst]);
11 |
12 | return <div>{currInput}</div>;
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:7:4
5 |
6 | useEffect(() => {
> 7 | setCurrInput(input);
| ^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
8 | }, [input]);
9 |
10 | return <div>{currInput}</div>;
```

View File

@@ -1,18 +1,11 @@
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
export default function Component({input = 'empty'}) {
export default function Component(input = 'empty') {
const [currInput, setCurrInput] = useState(input);
const localConst = 'local const';
useEffect(() => {
setCurrInput(input + localConst);
}, [input, localConst]);
setCurrInput(input);
}, [input]);
return <div>{currInput}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{input: 'test'}],
};

View File

@@ -6,8 +6,8 @@
import {useEffect, useState} from 'react';
function Component({firstName}) {
const [lastName, setLastName] = useState('Doe');
const [fullName, setFullName] = useState('John');
const [lastName, setLastName] = useState(null);
const [fullName, setFullName] = useState(null);
const middleName = 'D.';
@@ -36,13 +36,15 @@ export const FIXTURE_ENTRYPOINT = {
```
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: You might not need an effect. Derive values in render, not effects.
Derived values (From props and 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.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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
12 | }, [firstName, middleName, lastName]);
13 |
14 | return (

View File

@@ -2,8 +2,8 @@
import {useEffect, useState} from 'react';
function Component({firstName}) {
const [lastName, setLastName] = useState('Doe');
const [fullName, setFullName] = useState('John');
const [lastName, setLastName] = useState(null);
const [fullName, setFullName] = useState(null);
const middleName = 'D.';

View File

@@ -1,47 +0,0 @@
## Input
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component({initialName}) {
const [name, setName] = useState('');
useEffect(() => {
setName(initialName);
}, [initialName]);
return (
<div>
<input value={name} onChange={e => setName(e.target.value)} />
</div>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{initialName: '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-setter-call-outside-effect-no-error.ts:8:4
6 |
7 | useEffect(() => {
> 8 | setName(initialName);
| ^^^^^^^ 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)
9 | }, [initialName]);
10 |
11 | return (
```

View File

@@ -1,46 +0,0 @@
## Input
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function MockComponent({onSet}) {
return <div onClick={() => onSet('clicked')}>Mock Component</div>;
}
function Component({propValue}) {
const [value, setValue] = useState(null);
useEffect(() => {
setValue(propValue);
}, [propValue]);
return <MockComponent onSet={setValue} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{propValue: '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-setter-used-outside-effect-no-error.ts:11:4
9 | const [value, setValue] = useState(null);
10 | useEffect(() => {
> 11 | setValue(propValue);
| ^^^^^^^^ 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 | }, [propValue]);
13 |
14 | return <MockComponent onSet={setValue} />;
```

View File

@@ -3,7 +3,6 @@
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component({value}) {
const [localValue, setLocalValue] = useState('');
@@ -29,16 +28,18 @@ export const FIXTURE_ENTRYPOINT = {
```
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: You might not need an effect. Derive values in render, not effects.
error.derived-state-from-prop-with-side-effect.ts:8:4
6 |
7 | useEffect(() => {
> 8 | 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)
9 | document.title = `Value: ${value}`;
10 | }, [value]);
11 |
Derived values (From props: [value]) 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-prop-with-side-effect.ts:7:4
5 |
6 | useEffect(() => {
> 7 | setLocalValue(value);
| ^^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
8 | document.title = `Value: ${value}`;
9 | }, [value]);
10 |
```

View File

@@ -1,5 +1,4 @@
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component({value}) {
const [localValue, setLocalValue] = useState('');

View File

@@ -33,13 +33,15 @@ export const FIXTURE_ENTRYPOINT = {
```
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: You might not need an effect. Derive values in render, not effects.
Derived values (From props: [propValue]) 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.effect-contains-local-function-call.ts:12:4
10 |
11 | useEffect(() => {
> 12 | setValue(propValue);
| ^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
13 | localFunction();
14 | }, [propValue]);
15 |

View File

@@ -3,11 +3,9 @@
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component() {
function BadExample() {
const [firstName, setFirstName] = useState('Taylor');
const lastName = 'Swift';
const [lastName, setLastName] = useState('Swift');
// 🔴 Avoid: redundant state and unnecessary Effect
const [fullName, setFullName] = useState('');
@@ -18,11 +16,6 @@ function Component() {
return <div>{fullName}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
```
@@ -31,16 +24,18 @@ export const FIXTURE_ENTRYPOINT = {
```
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: You might not need an effect. Derive values in render, not effects.
error.invalid-derived-computation-in-effect.ts:11:4
9 | const [fullName, setFullName] = useState('');
10 | useEffect(() => {
> 11 | setFullName(firstName + ' ' + 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, lastName]);
13 |
14 | return <div>{fullName}</div>;
Derived values (From local state: []) 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:9:4
7 | const [fullName, setFullName] = useState('');
8 | useEffect(() => {
> 9 | setFullName(firstName + ' ' + lastName);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
10 | }, [firstName, lastName]);
11 |
12 | return <div>{fullName}</div>;
```

View File

@@ -1,9 +1,7 @@
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component() {
function BadExample() {
const [firstName, setFirstName] = useState('Taylor');
const lastName = 'Swift';
const [lastName, setLastName] = useState('Swift');
// 🔴 Avoid: redundant state and unnecessary Effect
const [fullName, setFullName] = useState('');
@@ -13,8 +11,3 @@ function Component() {
return <div>{fullName}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};

View File

@@ -29,13 +29,15 @@ export const FIXTURE_ENTRYPOINT = {
```
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: 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);
| ^^^^^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
10 | }, [props.prefix, props.value, props.suffix]);
11 |
12 | return <div>{displayValue}</div>;

View File

@@ -30,13 +30,15 @@ export const FIXTURE_ENTRYPOINT = {
```
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: 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);
| ^^^^^^^^^^^ 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)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This should be computed during render, not in an effect
11 | }, [props.firstName, props.lastName]);
12 |
13 | return <div>{fullName}</div>;

View File

@@ -6,15 +6,15 @@
import {useEffect, useState, useRef} from 'react';
export default function Component({test}) {
const [local, setLocal] = useState(0);
const [local, setLocal] = useState('');
const myRef = useRef(null);
useEffect(() => {
if (myRef.current) {
setLocal(test);
setLocal(test + 'Available');
} else {
setLocal(test + test);
setLocal(test + 'NotAvailable');
}
}, [test]);
@@ -23,7 +23,7 @@ export default function Component({test}) {
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{test: 4}],
params: [{test: 'testString'}],
};
```
@@ -37,7 +37,7 @@ import { useEffect, useState, useRef } from "react";
export default function Component(t0) {
const $ = _c(5);
const { test } = t0;
const [local, setLocal] = useState(0);
const [local, setLocal] = useState("");
const myRef = useRef(null);
let t1;
@@ -45,9 +45,9 @@ export default function Component(t0) {
if ($[0] !== test) {
t1 = () => {
if (myRef.current) {
setLocal(test);
setLocal(test + "Available");
} else {
setLocal(test + test);
setLocal(test + "NotAvailable");
}
};
@@ -73,10 +73,10 @@ export default function Component(t0) {
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ test: 4 }],
params: [{ test: "testString" }],
};
```
### Eval output
(kind: ok) 8
(kind: ok) testStringNotAvailable

View File

@@ -2,15 +2,15 @@
import {useEffect, useState, useRef} from 'react';
export default function Component({test}) {
const [local, setLocal] = useState(0);
const [local, setLocal] = useState('');
const myRef = useRef(null);
useEffect(() => {
if (myRef.current) {
setLocal(test);
setLocal(test + 'Available');
} else {
setLocal(test + test);
setLocal(test + 'NotAvailable');
}
}, [test]);
@@ -19,5 +19,5 @@ export default function Component({test}) {
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{test: 4}],
params: [{test: 'testString'}],
};

View File

@@ -1,41 +0,0 @@
## Input
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function BadExample() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// 🔴 Avoid: redundant state and unnecessary Effect
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
return <div>{fullName}</div>;
}
```
## 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-computation-in-effect.ts:11:4
9 | const [fullName, setFullName] = useState('');
10 | useEffect(() => {
> 11 | setFullName(firstName + ' ' + 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, lastName]);
13 |
14 | return <div>{fullName}</div>;
```

View File

@@ -1,15 +0,0 @@
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function BadExample() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
// 🔴 Avoid: redundant state and unnecessary Effect
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
return <div>{fullName}</div>;
}