[compiler] Don't throw calculate in render when there is a prop function call in effects

This commit is contained in:
Jorge Cabiedes Acosta
2025-09-23 14:08:48 -07:00
parent 1430e808cd
commit 2fa703de55
4 changed files with 88 additions and 43 deletions
@@ -300,6 +300,19 @@ 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;
}
}
}
seenBlocks.add(block.id);
@@ -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 <div>{value}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{propValue: 'test', onChange: () => {}}],
};
```
## 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 = <div>{value}</div>;
$[5] = value;
$[6] = t3;
} else {
t3 = $[6];
}
return t3;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ propValue: "test", onChange: () => {} }],
};
```
### Eval output
(kind: ok) <div>test</div>
@@ -1,43 +0,0 @@
## Input
```javascript
// @validateNoDerivedComputationsInEffects
import {useEffect, useState} from 'react';
function Component({propValue, onChange}) {
const [value, setValue] = useState(null);
useEffect(() => {
setValue(propValue);
onChange();
}, [propValue]);
return <div>{value}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{propValue: 'test', onChange: () => {}}],
};
```
## 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.effect-contains-prop-function-call-no-error.ts:7:4
5 | const [value, setValue] = useState(null);
6 | useEffect(() => {
> 7 | 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)
8 | onChange();
9 | }, [propValue]);
10 |
```