[英]Why is react setState method immutable?
Following comes from React tutorial:以下来自 React 教程:
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
This code changes copied state.squares
and assign it to orginal state.squares
.此代码更改复制
state.squares
并将其分配给原始state.squares
。 Finally this changes original state.squares
, so I think this is not different than mutable code like following:最后这改变了原来
state.squares
,所以我认为这与下面的可变代码没有什么不同:
this.state.squares[i] = 'X';
Is there some difference?有什么不同吗?
I do wonder about this question too.我也想知道这个问题。 But I find that the answers were unsatisfactory.
但我发现答案并不令人满意。 So here is my take
所以这是我的看法
The answer is actually written in the doc itself http://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly答案实际上写在文档本身http://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly
so first reason, setState()
triggers render()
所以第一个原因,
setState()
触发render()
second reason, state changing is asynchronous in React, so you might have some other components changing state behind the scene while you are still referring to the old unchanged state, which result in wrong state values第二个原因,React 中的状态更改是异步的,因此您可能有一些其他组件在幕后更改状态,而您仍然引用旧的未更改状态,这会导致错误的状态值
This code is immutable, because slice()
method is used.这段代码是不可变的,因为使用了
slice()
方法。 If you try:如果你试试:
someState = {squares: [1,2,3,4,5]}
squares = someState.squares.slice()
You'll get new array created by slice()
method.您将获得由
slice()
方法创建的新数组。
You can test it that way:你可以这样测试:
squares = someState.squares.slice()
squares2 = someState.squares
squares[0] = 9 // doesn't change someState
squares2[1] = 9 // changes someState
someState.squares // [1,9,3,4,5] - as I said
And if you have doubts about this.setState({squares: squares});
如果你对
this.setState({squares: squares});
有疑问- yes, of course after running this you have new state, but in fact this state is not modified old state object, but new object created from old parts. - 是的,当然运行这个之后你有新的状态,但实际上这个状态不是修改旧状态对象,而是从旧部分创建的新对象。 So if you try:
所以如果你尝试:
oldState = this.state
this.setState({squares: squares})
You'll see that new state will differ than saved old:您会看到新状态与保存的旧状态不同:
this.state == oldState //false
In case of this.state.squares[i] = 'X';
如果
this.state.squares[i] = 'X';
oldState
would be modified too and that is exactly what we call mutability. oldState
也会被修改,这正是我们所说的可变性。 All copied parts of old state changes with it and that causes many problems.旧状态的所有复制部分都随之更改,这会导致许多问题。
You can do this, but you should not, the reason behind is that, if you use你可以这样做,但你不应该这样做,背后的原因是,如果你使用
this.state.squares[i] = 'X';
It will be overridden with next它将被 next 覆盖
this.setState({squares: squares});
So, your app will not have accurate data.因此,您的应用程序将不会有准确的数据。
From Doc:来自文档:
Never mutate this.state directly, as calling
setState()
afterwards may replace the mutation you made.永远不要直接改变 this.state,因为之后调用
setState()
可能会替换你所做的改变。 Treat this.state as if it were immutable.将 this.state 视为不可变的。
Check more about this in https://facebook.github.io/react/docs/react-component.html#state在https://facebook.github.io/react/docs/react-component.html#state 中查看更多相关信息
Do not mutate the state directly, that's what the doc said.不要直接改变状态,这就是文档所说的。
I was coding a todo list and make the same mistake of mutating the state directly, but in my case I was using hooks.我正在编写一个待办事项列表,并犯了直接改变状态的同样错误,但在我的情况下,我使用了钩子。 I didn't understand why the screen does not re-render when I run setState.
我不明白为什么当我运行 setState 时屏幕没有重新渲染。 The state did mutate (confirmed by
console.log
) and even useEffect
ran because it detects an updated dependency.状态确实发生了变化(由
console.log
确认),甚至useEffect
运行,因为它检测到更新的依赖项。
React class that extends Purecomponent has the same behaviour too.The funny thing is, if I were to use class that extends React.Component and use this.setState function the app does rerender the screen.扩展 Purecomponent 的 React 类也有相同的行为。有趣的是,如果我使用扩展 React.Component 的类并使用 this.setState 函数,应用程序会重新渲染屏幕。
After I ask and learn, it turns out I need to treat states as immutable.在我询问和学习之后,事实证明我需要将状态视为不可变的。
This code : var newwrongArr = this.state.arr;
这段代码:
var newwrongArr = this.state.arr;
Does not copy anything, it only refers the value, the proof is that if you mutate newwrongArr
the state will also mutate.不复制任何东西,它只引用值,证明如果你改变
newwrongArr
状态也会改变。
If we want to copy, the code should be :如果我们要复制,代码应该是:
var newArr = [...this.state.arr]; //the three dots are spread operator
or some other function like Object.assign().
或其他一些函数,如
Object.assign().
My conclusion is, if we mutate newwrongArr
and setStatearr(newwrongArr)
, I think React hooks will decide that rerender is unnecessary because it consider newwrongArr
to be the same as the value of state (although the state changes, the rerender does not happen).我的结论是,如果我们改变
newwrongArr
和setStatearr(newwrongArr)
,我认为 React 钩子会决定 rerender 是不必要的,因为它认为newwrongArr
与 state 的值相同(尽管 state 发生了变化,但 rerender 不会发生)。 But if we were to copy the value with spread operator, then setState will consider rerender necessary which is the expected result.但是如果我们要使用扩展运算符复制值,那么 setState 会认为需要重新渲染,这是预期的结果。
Sorry for the long answer.对不起,答案很长。
Agree with @Karol Selak, I want to make thing more clearer with an example here:同意@Karol Selak 的观点,我想在这里举个例子让事情更清楚:
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
This code will do the following steps:此代码将执行以下步骤:
When we use, for example the useEffect hook as follows:当我们使用时,例如 useEffect hook 如下:
useEffect(() => {
// do something
}[state.squares])
React will compare old squares and new squares, in this case, 2 these are different because the address in memory is different. React 将比较旧方块和新方块,在这种情况下,2 这些是不同的,因为 memory 中的地址不同。 Then the useEffect will run.
然后 useEffect 将运行。
In the second case:在第二种情况下:
this.state.squares[i] = 'X';
The useEffect will not work useEffect 将不起作用
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.