小编典典

反应-不使用setState更改状态:必须避免吗?

reactjs

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

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

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_collected,那么也会更改this.state.data[0].is_collected。这发生在我打电话之前setState()

通常我应该这样做:

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

然后,我更改index处的值,如果为false则将其设置为true,否则将其设置为false:

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

并更改状态:

this.setState({data: data});

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

  1. 在这种情况下,是否必须创建深度克隆?(for var i in ...
  2. 如果没有,.slice(0)如果我的数组包含对象,则进行浅表克隆()是否有意义?更改是在数组内部的对象上进行的,因此浅表克隆仍会更改我的状态,就像copy(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;

阅读 280

收藏
2020-07-22

共1个答案

小编典典

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

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.datasetState多次调用,则可能会丢失数据:

    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

2020-07-22