React,是否绝对有必要将 React 状态视为不可变的?

React, is it absoluately necessary to treat react state as immutable?

我正在做这样的事情,想知道是否应该完全避免或接受它。

因为将状态视为不可变非常复杂..

第一个版本是我平时做的

render() {
  var { blogs } = this.state
  var blogNodes = blogs.map((blog, i) => {
    return (
      <div>
        {blog.name}
        <Checkbox
          checked={blog.checked}
          onChange={
            (e, {name, value, checked}) => {
              blog.checked = checked
              this.setState({blogs})
            }
          }
          />
      </div>
    )
    })                                                                                                                                                                                   
} ,  

第二个版本似乎是更正确的版本

render() {
  var { blogs } = this.state                                                                                                                                                       
  var blogNodes = blogs.map((blog, i) => {                                                                                                                                          

  return (                                                                                                                                                                          
     <div>
        {blog.name}
        <Checkbox
          checked={blog.checked}
          onChange={
            (e, {name, value, checked}) => {
              blog.checked = checked
              var newState = update(this.state, {
                blogs: {
                  i: {
                    $set: {
                      checked: checked
                    }
                  }
                }
              })
              this.setState(newState)
            }
          }
          />
      </div>
    )
  })
}

将 React 状态视为不可变是绝对必要的还是仅仅是出于性能原因?

如果我必须将其视为不可变的,是否有比我的第二个版本更简单的方法来执行上述操作? 当我们手头有 index(i) 时,它会更烦人。

Is it absolutely neccessary to treat react state as immutable?

取决于你所说的 "absolutely necessary" 的意思,但我认为可以肯定地说,如果 the authors straight up tell you it's "wrong" 在 React 中改变状态是一个 非常糟糕的主意 ].如果你继续这样做,你就会搬起石头砸自己的脚。性能调整是一个原因,但另一个原因很简单,如果您不遵循 React 的 API 规则,您将获得不可预测的行为,例如无意中覆盖数据或以难以调试的方式破坏渲染。

React 完全是关于可预测的渲染周期:使用 setState() 更改状态,这会在未来的某个时刻触发 render() 最新状态。

你的第二个例子还有问题。首先,您仍在通过设置 blog.checked = true 来改变状态 blog。其次,您将 整个 状态替换为 newState。这是一个问题的根源,因为 setState() 安排 一个补丁,所以你应该只替换你实际改变的道具 - setState({blogs}) 在您的情况——或者您可能与其他对 setState() 的调用发生冲突。最后,如果您正在访问当前 state,您应该真正使用回调形式 (second example) 以确保您正在使用的状态实际上是完全最新的版本。

使用 ES6 对象传播你可以这样做:

this.setState((prevState, props) => {
  const {blogs} = this.state;
  return {
    blogs: [
      ...blogs.slice(0, i), 
      { ...blogs[i], selected: true }
      ...blogs.slice(i)
    ]
  };
});

或使用 map() 仅通过索引替换一个元素:

this.setState((prevState, props) => {
  const {blogs} = this.state;
  return {
    blogs: blogs.map(
      (blog, index) => index == i ? { ...blog, selected: true } : blog
    )
  };
});

保持组件状态不可变是绝对必要的吗?不,但重要的是要理解为什么你应该这样做,特别是如果你在你的状态树中嵌套(比如你的例子),原因如下

当所述组件的状态(或道具)更新时,React 会更新组件,而关于 React 的最好的部分是您可以使用 shouldComponentUpdate() 生命周期事件完全控制何时应该发生此更新,所以。 .如果你正在改变你的状态对象,那么你将不得不做一些被称为深度股权检查的事情来弄清楚状态何时更新,这会随着你的状态对象而变得更糟里面有更多的嵌套,.

现在想象一下,将您的状态保持为不可变对象,您最终会得到这个漂亮的状态对象,它是明确且易于预测的,您可以相应地优化您的组件以获得更好的性能...

其次,默认情况下 React 不会进行深度股权检查以查看状态或道具是否已更新,因此您可能会听到他们告诉人们不要改变状态,就像其他人提到的那样,

现在要实现任何 React 组件,请始终记住,保持状态最小化是您可以获得的最佳 React 实践,因此最好只为组件提供渲染所需的那些值。

onChange(event, i) {
 const target = event.target;
 const value = target.type === 'checkbox' ? target.checked : target.value;
 this.props.handleChange(this.props.blog, value)
}

render() {
  var { blog } = this.state                                                                                                                                                       
  return (                                                                                                                                                                          
     <div>
        {blog.name}
        <Checkbox
          checked={blog.checked}
          onChange={this.onChange}
          />
      </div>
    )
}

然后在您的父博客组件中

handleChange(blog, value) {
 this.setState({
   blogs: [
    ...blogs,
    {...blog,  selected: value}
   ]
 })
}