React - 在不使用 setState 的情况下更改状态:必须避免吗?

IT技术 javascript arrays reactjs state setstate
2021-04-05 05:09:29

我的代码有效,但我有一个最佳实践问题:我在状态中有一组对象,用户交互将一次更改一个对象的值。据我所知,我不应该直接改变状态,我应该总是使用setState如果我想以任何代价避免这种情况,我将通过迭代深度克隆数组,并更改克隆。然后将状态设置为克隆。在我看来,避免改变我以后会改变的状态只会降低我的表现。

详细版本: this.state.data 是一个对象数组。它代表论坛中的主题列表,一个收藏按钮将切换,调用clickCollect()由于我在 state 中有一个数组,当我更改一个 item 的 is_collected 属性时,我需要创建一个数组的副本来使用,并且在更改为新值后,我可以将其设置为 state。

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

var data = this.state.data:这将复制指向数组的指针,push()、shift() 等将直接改变状态。双方datathis.state.data会受到影响。

var data = this.state.data.slice(0):这会创建一个浅层克隆,push 和 shift 不会改变状态,但在我的克隆中,我仍然有指向状态数组元素的指针。所以如果我改变data[0].is_collectedthis.state.data[0].is_collected也会改变。这发生在我打电话之前setState()

通常我应该这样做:

var data = []; 
for (var i in this.state.data) {
    data.push(this.state.data[i]); 
}

然后我更改索引处的值,当它为假时将其设置为真或为真时设置为假:

data[index].is_collected = !data[index].is_collected;

并更改状态:

this.setState({data: data});

考虑到我的数组比较大或非常大,我猜这次迭代会降低我的APP的性能。如果我知道出于任何原因这是正确的方式,我就会支付这笔费用。然而,在这个函数 ( clickCollect) 中,我总是将新值设置为状态,我不会等待一个错误的 API 响应,它会说停止进行更改。在所有情况下,新值都会进入状态。实际上,我setState只要求 UI 再次呈现。所以问题是:

  1. 在这种情况下,我是否必须创建深度克隆?( for var i in ...)
  2. 如果没有,.slice(0)如果我的数组包含对象,制作浅克隆 ( ) 是否有意义更改是在数组内部的对象上进行的,因此浅克隆仍然会更改我的状态,就像副本 ( data = this.state.data) 所做的那样。

我的代码被简化了,为了简单起见,API 调用被删掉了。

这是初学者的问题,因此也欢迎采用完全不同的方法。或其他问答的链接。

import React from 'react';

var ForumList = React.createClass({
  render: function() {
      return <div className="section-inner">
        {this.state.data.map(this.eachBox)}
      </div>
  },
  eachBox: function(box, i) {
    return <div key={i} className="box-door">
        <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}>
          {box.id}
        </div>
    </div>
  },
  getInitialState: function() {
    return {data: [
      {
        id: 47,
        is_collected: false
      },
      {
        id: 23,
        is_collected: false
      },
      {
        id: 5,
        is_collected: true
      }
    ]};
  },
  clickCollect: function(index) {
    var data = this.state.data.slice(0);
    data[index].is_collected = !data[index].is_collected;
    this.setState({data: data});
  }
});

module.exports = ForumList;
4个回答

就我个人而言,我并不总是遵守规则,如果您真的了解自己要做什么,那么我认为这不是问题。

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

在这种情况下,改变状态并setState像这样再次调用就可以了

this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});

你应该避免改变你的状态的原因是,如果你有一个对 的引用this.state.data,并且setState多次调用,你可能会丢失你的数据:

const myData = this.state.data
myData[0] = 'foo'
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })

myData[1] = 'bar' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`

如果你真的很关心这个,那就去immutable.js

直接静音状态破坏了 React 数据流的主要原则(它被设计为单向的),使您的应用程序非常脆弱,并且基本上忽略了整个组件生命周期。

所以,虽然没有什么能真正阻止你在没有 setState({}) 的情况下改变组件状态,但如果你想真正利用 React,你必须不惜一切代价避免这种情况,否则你将跳过库的核心功能之一。

如果您想遵循 react 最佳实践,则应该在更改任何属性时对所有数组进行浅拷贝。请查看“不可变”库实现。

但是,根据我的经验和我的观点,setState如果您有“shouldCompomenentUpdate”实现,则应该调用方法。如果你认为你的浅拷贝会消耗更多的资源,然后react虚拟 dom 检查,你可以这样做:

this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();

如果我理解你的问题,你有一个对象数组,当数组中单个对象的属性发生变化时,

  1. 创建数组的深度克隆并传递给 setState
  2. 创建一个浅克隆并传递给 setState

我刚刚检查了redux 示例 todo 应用程序,如果对象的单个属性发生更改,您必须创建该单个对象的新副本,而不是整个数组。我建议您阅读有关redux并在可能的情况下用于管理您的应用程序的状态。