ReactJS更新状态数组中的单个对象

14

我有一个名为this.state.devices的状态,它是一个包含device对象的数组。

假设我有一个函数:

updateSomething: function (device) {
    var devices = this.state.devices;
    var index = devices.map(function(d){
        return d.id;
    }).indexOf(device.id);

    if (index !== -1) {
       // do some stuff with device
       devices[index] = device;
       this.setState({devices:devices});
    }
}
问题在于每次调用this.updateSomething时,整个数组都会被更新,整个DOM也会被重新渲染。在我的情况下,这导致浏览器卡死,因为我几乎每秒钟调用一次这个函数,而且有很多device对象。然而,在每次调用时,只有一个或两个这些设备实际上会被更新。
我的选择是什么?
编辑:
在我的具体情况下,一个device是一个被定义如下的对象:
function Device(device) {
    this.id = device.id;
    // And other properties included
}

因此,state.devices 数组中的每个项都是这个 Device 的特定实例,也就是说,在某个地方我会有:

addDevice: function (device) {
    var newDevice = new Device(device);
    this.setState({devices: this.state.devices.push(device)});
}

关于如何更新 updateSomething,我的最新回答是:

updateSomething: function (device) {
    var devices = this.state.devices;
    var index = devices.map(function(d){
        return d.id;
    }).indexOf(device.id);

    if (index !== -1) {
       // do some stuff with device
       var updatedDevices = update(devices[index], {someField: {$set: device.someField}});
       this.setState(updatedDevices);
    }
}

现在的问题是我收到一个错误,说无法读取id的未定义值,并且它来自function Device();似乎调用了一个新的new Device()并且device没有被传递给它。


有一些方法可以防止每次渲染,例如shouldComponentUpdate,请查看此帖子https://www.codementor.io/reactjs/tutorial/understanding-react-js-rendering。此外,如果您找到一种将索引与设备保持一致而不是每次映射和搜索它的方法,您还将节省一些执行时间。您还在技术上更改了状态中的数组,应将其视为不可变的,建议使用.slice(),如https://dev59.com/A18d5IYBdhLWcg3wt0Oy所述。 - Dave Satch
4个回答

8
您可以使用 React 不可变助手
从文档中得知: 简单的 push 操作
var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray仍然是[1, 2, 3]。

所以对于你的示例,你需要做类似这样的事情:

if (index !== -1) {
    var deviceWithMods = {}; // do your stuff here
    this.setState(update(this.state.devices, {index: {$set: deviceWithMods }}));
}

根据您的设备模型的复杂程度,您可以直接在原地“修改”对象属性:

if (index !== -1) {
    this.setState(update(this.state.devices[index], {name: {$set: 'a new device name' }}));
}

我的 initialArray 是一个对象实例的数组;也就是说,我有 MyObject(),并且使用 initialArray.push(new MyObject(object)) 来添加它们。当我尝试您的方法时,在初始化期间,我会在此对象内部收到一个错误。它说无法读取未知属性;它正在寻找 object 中的字段。这个 update 是否正在重新创建每个对象,这就是为什么它失败的原因? - Kousha
我认为你正在改变状态的结构。在你的 var updatedDevices... 行之前检查 devices,在之后检查 updatedDevices。我认为结构会有所不同。 - Clarkie
即使我注释掉this.setState,我仍然会得到相同的错误。是help函数引发了这个错误。 - Kousha
让我们在聊天中继续这个讨论 - Clarkie

0

由于某些原因,以上答案对我不起作用。经过许多尝试,以下方法有效:

if (index !== -1) {
            let devices = this.state.devices
            let updatedDevice = {//some device}
            let updatedDevices = update(devices, {[index]: {$set: updatedDevice}})
            this.setState({devices: updatedDevices})
    }

我从 https://reactjs.org/docs/update.html 导入了 immutability-helper 中的 update,根据文档记录中的提示。


0
在我看来,使用React状态时,只应存储与“状态”真正相关的内容,例如打开、关闭等内容,但当然也有例外情况。
如果我是你,我会将设备数组作为一个变量单独提取出来,并在那里设置内容,这是我可能会做的事情:
var devices = [];

var MyComponent = React.createClass({
    ...
    updateSomething: function (device) {

        var index = devices.map(function(d){
            return d.id;
        }).indexOf(device.id);

        if (index !== -1) {
           // do some stuff with device
           devices[index] = device;

           if(NeedtoRender) {
               this.setState({devices:devices});
           }
        }
    }
});

0

我通过对要修改的对象进行数组切片,然后更新组件状态并再次推送修改后的对象来解决它。

就像下面的例子:

let urls = this.state.urls;

var index = null;

for (let i=0; i < urls.length; i++){
  if (objectUrl._id == urls[i]._id){
    index = i;  
  }
}

if (index !== null){
  urls.splice(index, 1);  
}

urls.push(objectUrl);

this.setState((state) => {
  return {urls: urls}
}); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接