Search code examples
javascriptreactjsreact-state-management

How is it possible that dynamic components overwrite each other's states?


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?


Solution

  • 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