Skip to content
Advertisement

How to update state of array element during listener in React?

I’m working on a web app that reads sensor data to gauge objects via socket.io.

The function addNewGauge() adds an element to the component’s array of gauges, but I also want to install a socket.io listener for that element.

The addNewGauge() function works, and the listener is picking up the event, which I know because the error below occurs for every socket event:

TypeError: Cannot read property ‘value’ of undefined occurs for each socket event I send.

I’m assuming this is because it can’t read the array index during the listener? How do I update the state of that specific element in the state every time?

import React from 'react'
import socketIOClient from "socket.io-client"

class WidgetContainer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      //Initial array of gauges
      gauges: [{
        index: 0,
        sensorType: 'temperature',
        units: "Fahrenheit"
      }],
      //Endpoint for socket data
      endpoint: 'http://127.0.0.1:4200'
    }
    this.addNewGauge = this.addNewGauge.bind(this)
  }

  componentDidMount() {
    const {endpoint} = this.state;
    //Fire up the socket when container is mounted
    this.widget_socket = socketIOClient(endpoint)
  }

  addNewGauge() {
    this.setState(state => {
      // ensure next gauge has a unique index
      const nextIndex = state.gauges.length;
      const gaugeList = state.gauges.concat({
        index: nextIndex,
        sensorType: 'temperature',
        units: "Fahrenheit"
      });
      // Set up a listener for that gauge's sensor type
      //BROKEN
     this.widget_socket.on(gaugeList[nextIndex].sensorType, (data) => {
        //Update state with new sensor value
        let newState = update(this.state.gauges, {
          nextIndex: {
            value: {
              $set: data
            }
          }
        })
        this.setState(newState);
      })

      return {
        gauges: gaugeList
      };
    });
  }

  render() {
    let gaugeList = this.state.gauges.map((gauge) =>
      <Gauge 
      key = {gauge.index}
      // Specify gauge properties from our saved state
      value = {gauge.value}
      units = {gauge.units}
      />
    );

    return ( 
            <div>
               {gaugeList}
               <Button key="addButton" 
                onClick={this.addNewGauge}
               >Add gauge</Button>
            </div>
    );
  }
}

export default WidgetContainer

Advertisement

Answer

I seem to have found a solution by

  1. Adding a separate callback and binding to the class
  2. Not using “update()”

When the state is modified, this callback for the socket works great:

            let widgetCallback = (data, index) => {
            let newState = this.state
            newState.gauges[index].value = data;
            this.setState(newState)
        }
        // bind the callback to the container
        widgetCallback.bind(this)
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement