我正在构建一个在线棋盘游戏,包括创建react应用程序、react钩子,并使用sockets.io在连接的用户之间传输数据(玩家位置、活跃玩家等(。逻辑流程是用户做出选择,该选择被添加到状态中的数组中,然后通过套接字将更新后的状态推送给所有连接的用户。问题是负责从后端接收套接字数据并更新每个连接用户的用户数据的useEffect侦听器触发的次数太多,而不是一次。
代码:
将呼叫发送到后端:
try {
console.log(currentCard, typeof(currentCard.title), tech)
setUser1Data({
...user1Data,
userTech: [...user1Data.userTech, currentCard.title]
});
} finally {
console.log(user1Data)
socket.emit("p1state", user1Data);
pass();
}
后端接收器/发射器:
socket.on("p1state", function(state) {
console.log(state)
io.emit("p1state", state)
})
客户端侦听器:
useEffect(() => {
socket.on("p1state", state => {
console.log("1")
setUser1Data({...user1Data, state});
});
}, [user1Data]);
我注意到一些"有趣"的事情:这个useEffect被激发了太多次。第一次启动时,它会按照应该的方式设置所有内容,但随后每次都会覆盖以前的设置,恢复到原始的user1Data状态对象。
此外,在后端,当客户端连接时,我有一个console.log。尽管我目前只在本地测试一个浏览器选项卡,但它仍在记录几个用户连接的事件。
useEffect
当前正在使用依赖数组中的状态,并在updater函数中设置相同的状态。正如你所看到的,这导致了一个无限循环。
useEffect(() => {
socket.on("p1state", state => {
console.log("1")
setUser1Data(userData => ({...userData, state}));
});
}, []);
相反,您可以使用state setter的函数版本,这样它就可以为您提供准确的prevState
,而不是依赖于闭包中的状态表示。
我也遇到了类似的问题。我解决了这个问题,使useEffect在每次卸载时关闭套接字(并在每次装载/更新后打开/重新打开(。这是我的代码:
useEffect(()=>{
const socket = io("http://localhost:3000")
socket.on(userId, (arg) => {
//stuff
});
return () => socket.emit('end'); //close socket on unmount
})