4

I have created dynamic Room components which are created based on a room object dispatched by Redux.

{
    rooms && rooms.map((room, index) => {
        const { name, temperature, humidity, timestamp } = room

        return (
            <Col key={index} xs={12} md={6}>
                <Room 
                    index={index}
                    name={name} 
                    temperature={temperature} 
                    humidity={humidity}
                />
            </Col>
        )
    })
}

The details of each room are mounted properly. I create a function to maintain 10 objects in an array. However, when the array is passed into Rechart, it seems that my components are updating on top of the same state.

class Room extends Component {
    linechart = () => {
        const { timestamp, temperature, humidity, name } = this.props
        const { chartData } = this.state

        if(chartData.length > 9) chartData.shift()
        chartData.push({
            name, 
            timestamp: moment(timestamp).format('mm:ss'), 
            temperature, 
            humidity})
    }
}

As you can see, the component details are displayed properly. However, the values of chartData are being stored in the same state, despite being unique components.

I ran the function with an interval of 1 second, and the logs show that the state is being updated in 0.5 second intervals. That means both <Room/> components are using the same <LineChart/> component. Does anyone know how to overcome this issue?

Leon Kho
  • 97
  • 2
  • 8
  • Your chartData seems to be fed by your component state, and not your props. – i.brod May 26 '19 at 00:20
  • `const { chartData } = this.state` should be `const { chartData } = Object.assign({}, this.state)` – mikeb May 26 '19 at 00:29
  • I don't think the problem lies with the use of states. I used an interval of 1 second, and the logs show that the state is being updated in 0.5 second intervals. That means both components are using the same component. – Leon Kho May 26 '19 at 00:46
  • I think you’re not supposed to mutate state, like you’re doing with `chartData.shift()`. – Ry- May 26 '19 at 01:15
  • the component Room has a state? If yes, why you are changing the chartData without use setState to store that? without setState, the render() not will be called to refresh the draw – Joao Polo May 26 '19 at 01:28
  • I see. I looked up on how I should shift and push states, and based on the answers provided, they were using the approach I used to do so. What is the proper way of shifting objects into state arrays? – Leon Kho May 26 '19 at 01:42

1 Answers1

3

In order to update items in an array, I'd recommend using the spread syntax. Spread syntax works by shallow copying the array, which keeps a reference to the original array, but allows you to overwrite any data types stored within it. This keeps the array immutable.

I'm not sure where you're getting your data from, but since you have a finite amount of rooms, then the array should already be structured like so:

data: [
   { 
     name: "Room1", 
     humidity: 11,
     tempature: 30, 
     timestamp: "Sat May 25 2019 22:23:06 GMT-0700",
   },
   { 
     name: "Room2", 
     humidity: 11,
     tempature: 25, 
     timestamp: "Sat May 25 2019 22:23:06 GMT-0700",
   },
   ...etc
]

Then you can simply store this to state and when needed map over the array and update its properties with incoming data. However, this assumes incoming data contains all the rooms. If you just need to update a specific room within the rooms array, then you can compare an id (or something that uniquely identifies it) to an incoming new data id.

For example, a new update comes in, but it's only updating Room1, then we could do something like so..

incoming data

data: [
  { 
    name: "Room1", 
    humidity: 11,
    tempature: 30, 
    timestamp: "Sat May 25 2019 22:23:06 GMT-0700",
   }
];

current data stored in state as "rooms"

 this.setState(prevState => ({
      ...prevState, // spread out any previous state not related to "rooms"
      rooms: prevState.rooms.map(room => { // map over "rooms" and pull out each "room" object
        return room.name === data[0].name // if the "room.name" matches incoming data's "name", then...
          ? { ...data[0] } // spread and overwrite it with incoming data
          : room; // else leave the "room" as is
      })
 }));

Using array prototypes like pop and shift mutate the original array stored within state and React doesn't handle mutations to its state. You can, however, clone the room array with Array.from() or simply create a new array instance and mutate this new instance with some array prototype functions, but you must then re-set this new instance array to state to overwrite the old array -- React handles this without a problem. It's just not as clean as the spread syntax.


Working example includes both spread syntax and Array.prototype options with a setInterval:

Edit Updating Values Inside Array

Working example of randomly updating one item within an array by an id with a setInterval:

Edit Randomly Updating Values Inside Array By Id

Matt Carlotta
  • 18,972
  • 4
  • 39
  • 51
  • Thank you for pointing this out. It seems the problem lied with how I mutated the states, where I shift() and pop() the states directly. My approach to overcome this issue was spreading the original state into another variable and mutating it, then setting the state in a similar fashion as how you've demonstrated. – Leon Kho May 27 '19 at 12:30