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 WidgetContainerAdvertisement
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)