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
- Adding a separate callback and binding to the class
- 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)