最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ReactJS updating a single object inside a state array - Stack Overflow

programmeradmin1浏览0评论

I have a state called this.state.devices which is an array of device objects.

Say I have a function

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});
    }
}

Problem here is that every time this.updateSomething is called, the entire array is updated, and so the entire DOM gets re-rendered. In my situation, this causes the browser to freeze as I am calling this function pretty every second, and there are many device objects. However, on every call, only one or two of these devices are actually updated.

What are my options?

EDIT

In my exact situation, a device is an object that is defined as follows:

function Device(device) {
    this.id = device.id;
    // And other properties included
}

So each item in the array of state.devices is a specific instant of this Device, i.e. somewhere I'd have:

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

My updated answer how on to updateSomething, I have:

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);
    }
}

Problem now is that I get an error that says cannot read the undefined value of id, and it is coming from the function Device(); it seems that a new new Device() is being called and the device is not passed to it.

I have a state called this.state.devices which is an array of device objects.

Say I have a function

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});
    }
}

Problem here is that every time this.updateSomething is called, the entire array is updated, and so the entire DOM gets re-rendered. In my situation, this causes the browser to freeze as I am calling this function pretty every second, and there are many device objects. However, on every call, only one or two of these devices are actually updated.

What are my options?

EDIT

In my exact situation, a device is an object that is defined as follows:

function Device(device) {
    this.id = device.id;
    // And other properties included
}

So each item in the array of state.devices is a specific instant of this Device, i.e. somewhere I'd have:

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

My updated answer how on to updateSomething, I have:

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);
    }
}

Problem now is that I get an error that says cannot read the undefined value of id, and it is coming from the function Device(); it seems that a new new Device() is being called and the device is not passed to it.

Share Improve this question edited Aug 27, 2015 at 16:04 Kousha asked Aug 27, 2015 at 0:12 KoushaKousha 36.2k59 gold badges186 silver badges313 bronze badges 1
  • There are ways to prevent rendering each time such as shouldComponentUpdate, check out this post codementor.io/reactjs/tutorial/understanding-react-js-rendering. In addition, you will save some execution time if you find a way to keep the index with the device instead of mapping and searching for it each time. You are also technically changing the array in state, which should be treated as immutable, suggest using .slice() as per stackoverflow.com/questions/26505064/… – Dave Satch Commented Aug 27, 2015 at 0:53
Add a comment  | 

4 Answers 4

Reset to default 8

You can use the react immutability helpers.

From the docs:

Simple push

var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray is still [1, 2, 3].

So for your example you will want to do something like this:

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

Depending on how complex your device model is you could just 'modify' the object properties in situ:

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

In my opinion with react state, only store things that's really related to "state", such as things turn on, off, but of course there are exceptions.

If I were you I would pull away the array of devices as a variable and set things there, so there is what I might do:

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});
           }
        }
    }
});

For some reason above answers didn't work for me. After many trials the below did:

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

And I imported update from immutability-helper based on the note from: https://reactjs.org/docs/update.html

I solve it doing a array splice in object I wish modify, before update component state and push of object modified again.

Like example below:

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}
}); 
发布评论

评论列表(0)

  1. 暂无评论