Given the following React functional component:
const DataLog: FC = (props) => {
const [events, setEvents] = useState<string[]>([]);
const pushEvent = (event: string) => {
const updatedEvents = [...events];
updatedEvents.push(event);
setEvents([...updatedEvents]);
};
useEffect(() => {
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
'http://localhost:9000'
);
socket.on('connect', () => {
console.log('Web socket connected');
});
socket.on('event1', (event: string) => {
pushEvent(event);
});
socket.on('event2', (event: string) => {
pushEvent(event);
});
}, []);
return (
<div>
{events.map((event: string, index: number) => {
return <div key={index}>{event}</div>;
})}
</div>
);
};
export default DataLog;
I would like to update the events
state variable each time an event1
or event2
message is received via the socket.io websocket connection. This works fine if the messages are coming in reasonably spaced out, but the issue arises when event1
and event2
, or multiple event1
or event2
messages come in very rapidly. The state update doesn't have enough time to complete before the next event is being processed resulting in the events
array being overwritten by the last call to setEvents
. Is there a way I can ensure each new message gets added to the events
state object? Do I need to manually batch state updates? Is there another hook I can use to preserve state between updates?
The pushEvent
function is a dependency of the useEffect
, but isn't in it's dependencies array. This means that the updates use the initial function which points to the original empty events
array.
You can add it as a dependency of useEffect()
:
useEffect(() => {
// your code
}, [pushEvent]);
However, this would cause the useEffect
to be called on each render, since pushEvent
is recreated on render, so you should probably avoid that.
Instead, set the state using functional updates to make the new state dependent on the previous one:
const DataLog: FC = (props) => {
const [events, setEvents] = useState<string[]>([]);
useEffect(() => {
const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
'http://localhost:9000'
);
socket.on('connect', () => {
console.log('Web socket connected');
});
socket.on('event1', (event: string) => {
setEvents(prev => [...prev, event]);
});
socket.on('event2', (event: string) => {
setEvents(prev => [...prev, event]);
});
}, []);
return (
<div>
{events.map((event: string, index: number) => {
return <div key={index}>{event}</div>;
})}
</div>
);
};