Reputation: 133
I am building an online boardgame usin create-react-app, react hooks, and am using sockets.io to transmit data (player location, active player, etc.) between connected users. The flow of the logic is that a user makes a choice, that choice gets added to an array in state, and then the updated state is pushed via sockets to all connected users. The problem is that the useEffect listener that is in charge of receiving the socket data from the back end and updating the user data on each connected user is firing too many times instead of just once.
Code:
Send call to the back end:
try {
console.log(currentCard, typeof(currentCard.title), tech)
setUser1Data({
...user1Data,
userTech: [...user1Data.userTech, currentCard.title]
});
} finally {
console.log(user1Data)
socket.emit("p1state", user1Data);
pass();
}
The back end receiver/emitter:
socket.on("p1state", function(state) {
console.log(state)
io.emit("p1state", state)
})
The client listener:
useEffect(() => {
socket.on("p1state", state => {
console.log("1")
setUser1Data({...user1Data, state});
});
}, [user1Data]);
Some "interesting" things I noticed: this useEffect is being fired too many times. The first time it fires it sets everything the way it should, but then each subsequent time it overwrites the previous setting, reverting to the original user1Data state object.
Also, on the back end, I have a console.log firing when a client connects. Even though I am testing only locally with one browser tab at the moment, it is still logging several user connected events.
Upvotes: 1
Views: 1788
Reputation: 13
I had a similar problem. I solved it making the useEffect close the socket every time it gets unmounted (and open/reopen after every mount/update. This was my code:
useEffect(()=>{
const socket = io("http://localhost:3000")
socket.on(userId, (arg) => {
//stuff
});
return () => socket.emit('end'); //close socket on unmount
})
Upvotes: 0
Reputation: 19204
The useEffect
is currently using the state in the dependency array and is setting the same state in the updater function. As you can see, this leads to an infinite loop.
useEffect(() => {
socket.on("p1state", state => {
console.log("1")
setUser1Data(userData => ({...userData, state}));
});
}, []);
Instead you can use the function version of state setter so that it gives you the accurate prevState
instead of relying on state representation in closure.
Upvotes: 2