I have a react component like this, where I have a websocket set up, and on websocket events I am trying to update state inside of Redux
const Body = () => {
// Hook for Redux dispatch
const dispatch = useDispatch();
// REDUX reducers
const workCenterAvailabilityQueue = useSelector(selectAllWorkCenterAvailabilities)
// Add or Update workCenterAvailability
let existingWorkCenter;
const workCenterAvailabilityHandler = (id, data) => {
console.log('id = ', id)
console.log('data = ', data)
console.log(workCenterAvailabilityQueue)
existingWorkCenter = workCenterAvailabilityQueue.find(bm => bm.WorkCenter.WorkCenter_ID === id);
console.log(existingWorkCenter)
if (existingWorkCenter) {
console.log('WorkCenter exists, updating data!')
dispatch(updateWorkCenterAvailability({
id: existingWorkCenter.id,
changes: { Connection_Status: data.Connection_Status }})
);
}
else {
console.log('WorkCenter does not exist, creating one!')
dispatch(addWorkCenterAvailability(data));
}
}
// Debugging
useEffect(() => {
console.log('Updated state:', workCenterAvailabilityQueue);
if(workCenterAvailabilityQueue[10]){
console.log('workCenterAvailabilityQueue[10] -> ', workCenterAvailabilityQueue[10])
}
console.log('\n')
}, [workCenterAvailabilityQueue]);
// Web Socket setup
useEffect(() => {
console.log('connecting to Flask socket')
const socket = io('http://localhost:5000');
console.log('Start listening')
socket.on('workCenterAvailability', (data) => {
workCenterAvailabilityHandler(data.WorkCenter.WorkCenter_ID, data)
});
console.log('emitting message')
socket.emit('requestWorkCenterAvailability');
console.log('emitted!')
// Clean up function
return () => {
socket.disconnect();
};
}, []);
So my problem is that when I try to use the handler function, the workCenterAvailabilityQueue
always come back as an empty array, so when I try to find an object in the Queue it always fails
However based on the useEffect hook I have all the way at the bottom for debugging purposes, I can see that each time I add something to the Queue, it is in fact added to Redux
Why does my workCenterAvailabilityQueue
not contain live information?
The second useEffect
hook calls workCenterAvailabilityHandler
in a "workCenterAvailability"
event callback handler, which has a stale Javascript closure over the selected workCenterAvailabilityQueue
state.
You can resolve this issue a couple of ways:
Import the store object and get the current state value in the workCenterAvailabilityHandler
callback handler.
import store from '../path/to/store';
...
// Add or Update workCenterAvailability
const workCenterAvailabilityHandler = (id, data) => {
const state = store.getState();
const const workCenterAvailabilityQueue =
selectAllWorkCenterAvailabilities(state);
const existingWorkCenter = workCenterAvailabilityQueue.find(
bm => bm.WorkCenter.WorkCenter_ID === id
);
if (existingWorkCenter) {
dispatch(updateWorkCenterAvailability({
id: existingWorkCenter.id,
changes: { Connection_Status: data.Connection_Status }
}));
} else {
dispatch(addWorkCenterAvailability(data));
}
};
...
// Web Socket setup
useEffect(() => {
const socket = io('http://localhost:5000');
socket.on('workCenterAvailability', (data) => {
workCenterAvailabilityHandler(data.WorkCenter.WorkCenter_ID, data)
});
socket.emit('requestWorkCenterAvailability');
// Clean up function
return () => {
socket.disconnect();
};
}, []);
...
Use a React ref to cache the current workCenterAvailabilityQueue
state value.
...
const workCenterAvailabilityQueue = useSelector(
selectAllWorkCenterAvailabilities
);
const workCenterAvailabilityQueueRef = React.useRef();
useEffect(() => {
workCenterAvailabilityQueueRef.current = workCenterAvailabilityQueue;
}, [workCenterAvailabilityQueue]);
...
// Add or Update workCenterAvailability
const workCenterAvailabilityHandler = (id, data) => {
const const workCenterAvailabilityQueue =
workCenterAvailabilityQueueRef.current;
const existingWorkCenter = workCenterAvailabilityQueue.find(
bm => bm.WorkCenter.WorkCenter_ID === id
);
if (existingWorkCenter) {
dispatch(updateWorkCenterAvailability({
id: existingWorkCenter.id,
changes: { Connection_Status: data.Connection_Status }
}));
} else {
dispatch(addWorkCenterAvailability(data));
}
};
// Web Socket setup
useEffect(() => {
const socket = io('http://localhost:5000');
socket.on('workCenterAvailability', (data) => {
workCenterAvailabilityHandler(data.WorkCenter.WorkCenter_ID, data)
});
socket.emit('requestWorkCenterAvailability');
// Clean up function
return () => {
socket.disconnect();
};
}, []);
...