Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05c8f06c6b | ||
|
|
78aa14f401 |
@@ -18,6 +18,7 @@ import {
|
||||
CallExpression,
|
||||
Instruction,
|
||||
isUseStateType,
|
||||
isUseRefType,
|
||||
} from '../HIR';
|
||||
import {eachInstructionLValue, eachInstructionOperand} from '../HIR/visitors';
|
||||
import {isMutable} from '../ReactiveScopes/InferReactiveScopeVariables';
|
||||
@@ -284,6 +285,11 @@ 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) &&
|
||||
@@ -300,6 +306,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,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
|
||||
@@ -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,45 +0,0 @@
|
||||
|
||||
## 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'}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 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-ref-and-state-no-error.ts:10:4
|
||||
8 |
|
||||
9 | useEffect(() => {
|
||||
> 10 | setLocal(myRef.current + test);
|
||||
| ^^^^^^^^ 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 | }, [test]);
|
||||
12 |
|
||||
13 | return <>{local}</>;
|
||||
```
|
||||
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects
|
||||
import {useEffect, useState, useRef} from 'react';
|
||||
|
||||
export default function Component({test}) {
|
||||
const [local, setLocal] = useState(0);
|
||||
|
||||
const myRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (myRef.current) {
|
||||
setLocal(test);
|
||||
} else {
|
||||
setLocal(test + test);
|
||||
}
|
||||
}, [test]);
|
||||
|
||||
return <>{local}</>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{test: 4}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 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.ref-conditional-in-effect-no-error.ts:11:6
|
||||
9 | useEffect(() => {
|
||||
10 | if (myRef.current) {
|
||||
> 11 | setLocal(test);
|
||||
| ^^^^^^^^ 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 | } else {
|
||||
13 | setLocal(test + test);
|
||||
14 | }
|
||||
|
||||
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.ref-conditional-in-effect-no-error.ts:13:6
|
||||
11 | setLocal(test);
|
||||
12 | } else {
|
||||
> 13 | setLocal(test + test);
|
||||
| ^^^^^^^^ 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)
|
||||
14 | }
|
||||
15 | }, [test]);
|
||||
16 |
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
// @validateNoDerivedComputationsInEffects
|
||||
import {useEffect, useState, useRef} from 'react';
|
||||
|
||||
export default function Component({test}) {
|
||||
const [local, setLocal] = useState(0);
|
||||
|
||||
const myRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (myRef.current) {
|
||||
setLocal(test);
|
||||
} else {
|
||||
setLocal(test + test);
|
||||
}
|
||||
}, [test]);
|
||||
|
||||
return <>{local}</>;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{test: 4}],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 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(0);
|
||||
|
||||
const myRef = useRef(null);
|
||||
let t1;
|
||||
let t2;
|
||||
if ($[0] !== test) {
|
||||
t1 = () => {
|
||||
if (myRef.current) {
|
||||
setLocal(test);
|
||||
} else {
|
||||
setLocal(test + 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: 4 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
### Eval output
|
||||
(kind: ok) 8
|
||||
Reference in New Issue
Block a user