我有以下组件:
function SocketClient() {
const [socket, setSocket]= React.useState(null);
const [msg, setMsg] = React.useState([]);
function handleMessage(event) {
setMsg([...msg, String(event.data)]);
}
if(!socket){
const newSocket = new WebSocket("ws://localhost:8000/ws");
newSocket.addEventListener("open", function(){
newSocket.send("Hello Server!");
});
newSocket.addEventListener("message", handleMessage);
setSocket(newSocket);
}
function click() {
if (socket) {
socket?.send("hooooooonk");
}
}
return (
<div>
<p>Shared Component</p>
<button onClick={click}>HONK</button>
<pre><code>
{JSON.stringify(msg, null, " ")}
</code></pre>
</div>
);
}
我一直在尝试不同的方式在 React 组件中创建 websocket 连接。这是我尝试过的几种方法之一,我将套接字存储在本地状态,然后仅在当前没有套接字时创建一个套接字 (
if(!socket) {...
),以避免每次组件创建新连接刷新。
但是,当我在条件语句中添加事件侦听器时,我遇到了以下问题:
msg
变量(也是局部状态变量)卡在其初始值。
msg
是一个空数组。msg
的值设置为 ["Hello Server!"]
,这反映在组件的主体上。msg
的值为 ["Hello Server!", "hooooooonk"]
,但最后一个值只是不断替换现有的值。handleMessage
运行时它都将 msg
的值读取为空数组(或我将其初始化为的任何值)。所以我的问题是,为什么会这样?我对闭包的理解是
handleMessage
应该不断读取msg
的更新值。我的理解是错误的还是 hooks API 引入的东西?
使用 useState setter 的函数形式总是最安全的:
setMsg(msg => [...msg, String(event.data)]);
这将始终获取变量的当前值作为参数,避免了很多常见的陷阱。
我相信在你的情况下,陷阱是你每次渲染组件时都重新定义了
handleMessage
函数,而msg
在那个时候指的是什么不清楚 - 似乎在渲染时反应仍然使用默认值msg
。还有其他方法可以避免这个陷阱(我认为useCallback
在这里也可能有效),但以上可能是最好和最紧凑的解决方案。
我也会把你的
if(!socket){
块改为useEffect。