为了让 ws 在 Next.js 上工作,需要做很多事情。
首先,重要的是要意识到我希望我的 ws 代码在哪里运行。Next.js 上的 React 代码在两种环境中运行:在服务器上(构建页面时或使用 ssr 时)和客户端上。
在页面构建时建立 ws 连接几乎没有用处,这就是为什么我将只介绍客户端 ws。
全局 Websocket 类是仅浏览器的功能,服务器上不存在。这就是为什么我们需要在代码在浏览器中运行之前阻止任何实例化。一种简单的方法是:
export const isBrowser = typeof window !== "undefined";
export const wsInstance = isBrowser ? new Websocket(...) : null;
此外,您不需要使用 react 上下文来保存实例,完全可以将其保持在全局范围内并导入实例,除非您希望延迟打开连接。
如果您仍然决定使用 react 上下文(或在 react 树中的任何位置初始化 ws 客户端),记住实例很重要,这样它就不会在每次更新 react 节点时创建。
const wsInstance = useMemo(() => isBrowser ? new Websocket(...) : null, []);
或者
const [wsInstance] = useState(() => isBrowser ? new Websocket(...) : null);
在react中创建的任何事件处理程序注册都应该包含在一个useEffect
带有删除事件侦听器的返回函数中,这是为了防止内存泄漏,并且应该指定一个依赖数组。如果您的组件被卸载,useEffect
钩子也会删除事件监听器。
如果您希望重新初始化 ws 并处理当前连接,则可以执行类似于以下操作:
const [wsInstance, setWsInstance] = useState(null);
// Call when updating the ws connection
const updateWs = useCallback((url) => {
if(!browser) return setWsInstance(null);
// Close the old connection
if(wsInstance?.readyState !== 3)
wsInstance.close(...);
// Create a new connection
const newWs = new WebSocket(url);
setWsInstance(newWs);
}, [wsInstance])
// (Optional) Open a connection on mount
useEffect(() => {
if(isBrowser) {
const ws = new WebSocket(...);
setWsInstance(ws);
}
return () => {
// Cleanup on unmount if ws wasn't closed already
if(ws?.readyState !== 3)
ws.close(...)
}
}, [])