mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add test case for invalid lambdas
This is the example we discussed in our design sync.
```javascript
function Component(props) {
const [x, setX] = useState({ value: "" });
const onChange = (e) => {
// INVALID! should use copy-on-write and pass the new value
x.value = e.target.value;
setX(x);
};
return <input value={x.value} onChange={onChange} />;
}
```
Here `onChange` is a mutable lambda, and it should be invalid to pass a mutable
lambda where a frozen value is expected. This is because unlike other value
types, you cannot freeze a lambda — the only choice is to not call it at all.
Note that there is a harder case to catch:
```js
function Component(props) {
const [x, setX] = useState({ value: "" });
const onChange = (e) => {
// INVALID! should use copy-on-write and pass the new value
x.value = e.target.value;
setX(x);
};
const x = constructAValueThatMaybeAliasesItsInput(onChange);
return <input value={x.value} onChange={x.maybeGetTheLambdaBack()} />;
}
```
This case demonstrates how mutable lambdas can be captured and then accessed
later — the analysis to catch this case is more sophisticated bc it involves
inferring that `x` aliases a mutable lambda. But we also can't be sure that `x`
does alias the lambda, so disallowing this code could prevent a lot of valid
code from compiling. My hypothesis is that we should start with at least
validating the example at the top, while allowing the second case for now.
This commit is contained in:
+62
@@ -0,0 +1,62 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const [x, setX] = useState({ value: "" });
|
||||
const onChange = (e) => {
|
||||
// INVALID! should use copy-on-write and pass the new value
|
||||
x.value = e.target.value;
|
||||
setX(x);
|
||||
};
|
||||
return <input value={x.value} onChange={onChange} />;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(7);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = { value: "" };
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
const [x, setX] = useState(t0);
|
||||
const c_1 = $[1] !== x;
|
||||
const c_2 = $[2] !== setX;
|
||||
let t1;
|
||||
if (c_1 || c_2) {
|
||||
t1 = (e) => {
|
||||
// INVALID! should use copy-on-write and pass the new value
|
||||
x.value = e.target.value;
|
||||
setX(x);
|
||||
};
|
||||
$[1] = x;
|
||||
$[2] = setX;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const onChange = t1;
|
||||
const c_4 = $[4] !== x.value;
|
||||
const c_5 = $[5] !== onChange;
|
||||
let t2;
|
||||
if (c_4 || c_5) {
|
||||
t2 = <input value={x.value} onChange={onChange} />;
|
||||
$[4] = x.value;
|
||||
$[5] = onChange;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
function Component(props) {
|
||||
const [x, setX] = useState({ value: "" });
|
||||
const onChange = (e) => {
|
||||
// INVALID! should use copy-on-write and pass the new value
|
||||
x.value = e.target.value;
|
||||
setX(x);
|
||||
};
|
||||
return <input value={x.value} onChange={onChange} />;
|
||||
}
|
||||
Reference in New Issue
Block a user