[hir] Improve error message for mutating state

This commit is contained in:
Sathya Gunasekaran
2024-03-19 17:06:17 +00:00
parent 9358aeab34
commit b5a7fe4e1c
8 changed files with 81 additions and 1 deletions
@@ -261,6 +261,7 @@ const BUILTIN_HOOKS: Array<[string, BuiltInType]> = [
calleeEffect: Effect.Read,
hookKind: "useState",
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.State,
}),
],
[
@@ -1122,6 +1122,11 @@ export enum ValueReason {
*/
Context = "context",
/**
* A value returned from `useState`
*/
State = "state",
/**
* Props of a component or arguments of a hook.
*/
@@ -1768,6 +1768,8 @@ function getWriteErrorReason(abstractValue: AbstractValue): string {
return "Mutating a value returned from a function that should not be mutated.";
} else if (abstractValue.reason.has(ValueReason.ReactiveFunctionArgument)) {
return "Mutating props or hook arguments is not allowed. Consider using a local variable instead.";
} else if (abstractValue.reason.has(ValueReason.State)) {
return "Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead.";
} else {
return "This mutates a variable that React considers immutable.";
}
@@ -21,7 +21,7 @@ function Component(props) {
3 | const onChange = (e) => {
4 | // INVALID! should use copy-on-write and pass the new value
> 5 | x.value = e.target.value;
| ^^^^^^^ [ReactForget] InvalidReact: Mutating a value returned from a function that should not be mutated. (5:5)
| ^^^^^^^ [ReactForget] InvalidReact: Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead. (5:5)
6 | setX(x);
7 | };
8 | return <input value={x.value} onChange={onChange} />;
@@ -0,0 +1,29 @@
## Input
```javascript
import { useState } from "react";
function Foo() {
const [state, setState] = useState({ foo: { bar: 3 } });
const foo = state.foo;
foo.bar = 1;
return state;
}
```
## Error
```
4 | const [state, setState] = useState({ foo: { bar: 3 } });
5 | const foo = state.foo;
> 6 | foo.bar = 1;
| ^^^ [ReactForget] InvalidReact: Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead. (6:6)
7 | return state;
8 | }
9 |
```
@@ -0,0 +1,8 @@
import { useState } from "react";
function Foo() {
const [state, setState] = useState({ foo: { bar: 3 } });
const foo = state.foo;
foo.bar = 1;
return state;
}
@@ -0,0 +1,28 @@
## Input
```javascript
import { useState } from "react";
function Foo() {
let [state, setState] = useState({});
state.foo = 1;
return state;
}
```
## Error
```
3 | function Foo() {
4 | let [state, setState] = useState({});
> 5 | state.foo = 1;
| ^^^^^ [ReactForget] InvalidReact: Mutating a value returned from 'useState()', which should not be mutated. Use the setter function to update instead. (5:5)
6 | return state;
7 | }
8 |
```
@@ -0,0 +1,7 @@
import { useState } from "react";
function Foo() {
let [state, setState] = useState({});
state.foo = 1;
return state;
}