I'm trying to add a data to a table of some operations in React via WebSockets. I even get the new data from WebSocket successfully. I stuck with the question how to add a new data to existing. When I recieve new data in websocket response, my const operationList becomes empty.
Have a look at my code:
const [operationsList, setOperationsList] = useState([{}] )
// Here I get the existing data from backend API and store to operationsList. It works
async function fetchOperations(activeID) {
if (activeID !== false) {
const response = await axios.get(
`http://127.0.0.1:8000/api/operations/?made_by=${activeID}`
)
setOperationsList(response.data)
}
}
useEffect(() => {
setIsOperationsLoading(true)
fetchOperations(activeID)
.then(() => {setIsOperationsLoading(false)})
.catch((e) => {setOperationsError(e)})
},[activeID])
// Here I subscribe to websockets to get new data for adding to operationsList
useEffect(() => {
const ws = new WebSocket('ws://127.0.0.1:8000/ws/')
ws.addEventListener('message', (e) => {
const response = JSON.parse(e.data)
console.log(response) // Here I see new data. It's ok
console.log(operationsList) // All of the sudden operationsList become empty
})
ws.onopen = () => {
ws.send(JSON.stringify({
action: "subscribe_to_operations_activity",
request_id: new Date().getTime(),
}))
}
}, [])
I thought that in my second useEffect I could just add response data from WebSocket like setOperationsList([response, operationsList]). But operationsList is empty, so I've got just a new data in the table. How to fix it?
The second useEffect
hook runs only once when the component mounts, you are logging the initial operationsList
state value closed over in callback scope. In other words, it's a stale enclosure over the operationsList
state.
I'm guessing it's at this point you are wanting to append response
to the operationsList
state. You can use a functional state update to correctly access the previous state and append to it.
You may also want to unsubscribe to the "message"
event in the useEffect
hook's cleanup function. This is to prevent resource leaks and attempts to update state of unmounted components.
useEffect(() => {
const ws = new WebSocket('ws://127.0.0.1:8000/ws/');
const handler = (e) => {
const response = JSON.parse(e.data);
setOperationsList(operationsList => [
...operationsList, // <-- shallow copy previous state
response, // <-- append new data
]);
};
ws.addEventListener('message', handler);
ws.onopen = () => {
ws.send(JSON.stringify({
action: "subscribe_to_operations_activity",
request_id: new Date().getTime(),
}));
};
return () => {
ws.removeEventListener('message', handler);
};
}, []);