简体   繁体   English

为什么 react setState 方法是不可变的?

[英]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#statehttps://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).我的结论是,如果我们改变newwrongArrsetStatearr(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:此代码将执行以下步骤:

  • Create a new squares variable that is placed in memory, for example, at 12345创建一个新的 squares 变量,放置在 memory,例如 12345
  • Set item at index i to 'X' (at this point, old squares and new squares also got the change because the object in the array is a reference)将索引 i 处的项目设置为'X'(此时,旧方块和新方块也得到了变化,因为数组中的 object 是引用)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM