React - 在不使用setState的情况下更改状态:必须避免它吗? [英] React - Changing the state without using setState: Must avoid it?

查看:511
本文介绍了React - 在不使用setState的情况下更改状态:必须避免它吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

My code works, but I have a best practice question: I have an array of objects in the state, and a user interaction will change a value of one object at a time. As far as I know, I'm not supposed to change the state directly, i should always use setState instead. If I want to avoid that with any price, I will deep clone the array by iteration, and change the clone. Then set the state to the clone. In my opinion avoiding to change the state that I will change later anyway is just decreasing my performance.

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

Detailed version: this.state.data is an array of objects. It represents a list of topics in a forum, and a Favorite button will toggle, calling clickCollect(). Since I have an array in the state, when I change the is_collected property of one item, I need to create a copy of the array to work with, and after changing to the new value, I can set it to the 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()等会直接改变状态。 数据 this.state.data 都会受到影响。

var data = this.state.data : This would copy the pointer to the array and push(), shift(), etc would alter the state directly. Both data and this.state.data will be affected.

var data = this.state.data.slice(0):这使得浅层克隆,推送和移位不会改变状态,但在我的克隆中我仍然有指向状态数组元素的指针。因此,如果我更改 data [0] .is_collected this.state.data [0] .is_collected 将更改为好。这是在我调用 setState()之前发生的。

var data = this.state.data.slice(0) : This makes a shallow clone, push and shift doesn't change the state but in my clone I still have pointers to the elements of the state's array. So if I change data[0].is_collected, this.state.data[0].is_collected gets changed as well. This happens before I call setState().

通常我应该这样做:


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


然后我改变索引处的值,如果为假,则将其设置为true;如果为真,则将其设置为true:

Then I change the value at index, setting it to true when it's false or false when it's true:


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


并更改状态:


this.setState({data: data});


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

Consider my array is relatively big or enormously big, I guess this iteration will reduce the performance of my APP. I would pay that cost if I knew that it is the right way for any reason. However, in this function (clickCollect) I always set the new value to the state, I'm not waiting for a false API response that would say to stop making the change. In all cases, the new value will get into the state. Practically I call setState only for the UI to render again. So the questions are:


  1. 在这种情况下我是否必须创建深度克隆? ( for var i in ...

  2. 如果没有,那么制作浅层克隆是否有意义( .slice(0))如果我的数组包含对象?正在对数组内部的对象进行更改,因此浅层克隆仍会更改我的状态,就像副本( data = this.state.data )一样。

  1. Do I have to create the deep clone in this case? (for var i in ...)
  2. If not, does it make sense to make a shallow clone (.slice(0)) if my array contains objects? The changes are being made on the objects inside of the array, so the shallow clone still changes my state, just like a copy (data = this.state.data) would do.

为简单起见,我的代码已经过简化,API调用被删除。

My code is simplified and API calls are cut out for simplicity.

这是一个初学者的问题,所以也欢迎采用完全不同的方法。或链接到其他Q& A。

This is a beginner's question, so a totally different approach is also welcome. Or links to other Q & A.


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;



推荐答案

我个人不喜欢永远不要遵守规则,如果你真的明白你想要做什么,那么我不认为这是一个问题。

Personally I don't always follow the rule, if you really understand what you are trying to do then I don't think it's a problem.

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

在这种情况下,改变状态并调用 setState 再次像这样很好

In this case, mutating state and calling the setState again like this is fine

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

你应该避免改变你的州的原因是,如果你引用了 this.state.data ,多次调用 setState ,您可能会丢失数据:

The reason you should avoid mutating your state is that if you have a reference to this.state.data, and calling setState multiple times, you may lose your data:

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

If you really concerned about this, just go for immutable.js

这篇关于React - 在不使用setState的情况下更改状态:必须避免它吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆