首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >WebSockets与Next.js的结合

WebSockets与Next.js的结合
EN

Stack Overflow用户
提问于 2021-07-05 23:42:25
回答 2查看 25.7K关注 0票数 19

我想知道使用WebSockets页面连接到我的Next.js服务器的最佳方法是什么?我希望用户可以使用一个连接浏览页面,当他关闭页面时,他也会关闭WebSockets连接。我尝试使用React的上下文API:

代码语言:javascript
运行
复制
const WSContext = createContext(null);

const Wrapper = ({ children }) => {
  const instance = WebSocket("ws://localhost:3000/ws");

  return <WSContext.Provider value={instance}>{children}</WSContext.Provider>;
};

export const useWS = () => useContext(WSContext);

export default Wrapper;

它工作得很好,但在创建连接时却不是这样。基本的new WebSocket语法不起作用,所以我必须使用第三方库,比如我不喜欢的react-use-websocket。同样令人不安的是,我无法关闭连接。上下文不知道页面何时关闭,库也不提供关闭连接的挂钩。

我想知道在Next.js中处理Next.js连接的最佳方法是什么。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-07-12 10:10:16

要让ws在Next.js上工作,需要做很多事情。

首先,重要的是要意识到我希望我的ws代码在哪里运行。在Next.js上运行的代码在两个环境中运行:在服务器上(在构建页面时或在使用ssr时)和在客户机上。

在页面构建时建立ws连接几乎没有什么用处,这就是为什么我将只涉及客户端ws。

全局Websocket类是浏览器唯一的特性,在服务器上不存在。这就是为什么我们需要阻止任何实例化,直到代码在浏览器中运行。这样做的一个简单方法是:

代码语言:javascript
运行
复制
export const isBrowser = typeof window !== "undefined";
代码语言:javascript
运行
复制
export const wsInstance = isBrowser ? new Websocket(...) : null;

另外,不需要使用react上下文来保存实例,完全可以将其保持在全局范围内并导入实例,除非您希望延迟打开连接。

如果您仍然决定使用react上下文(或者在react树中的任何位置初始化ws客户机),那么实例进行回溯是很重要的,这样就不会在tree节点的每个更新上创建它。

代码语言:javascript
运行
复制
const wsInstance = useMemo(() => isBrowser ? new Websocket(...) : null, []);

代码语言:javascript
运行
复制
const [wsInstance] = useState(() => isBrowser ? new Websocket(...) : null);

在react中创建的任何事件处理程序注册都应该包装在一个带有返回函数的useEffect中,该函数移除事件侦听器,这是为了防止内存泄漏,还应该指定一个依赖数组。如果您的组件已卸载,useEffect钩子也将删除事件侦听器。

如果希望重新启用ws并释放当前连接,则可以执行以下类似操作:

代码语言:javascript
运行
复制
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(...)
 }
}, [])
票数 50
EN

Stack Overflow用户

发布于 2021-12-02 15:36:09

我配置了这样的上下文:

代码语言:javascript
运行
复制
import { createContext, useContext, useMemo, useEffect, ReactNode } from 'react';

type WSProviderProps = { children: ReactNode; url: string };

const WSStateContext = createContext<WebSocket | null>(null);

function WSProvider({ children, url }: WSProviderProps): JSX.Element {
  const wsInstance = useMemo(
    () => (typeof window != 'undefined' ? new WebSocket(`ws://127.0.0.1:8001/ws${url}`) : null),
    []
  );

  useEffect(() => {
    return () => {
      wsInstance?.close();
    };
  }, []);

  return <WSStateContext.Provider value={wsInstance}>{children}</WSStateContext.Provider>;
}

function useWS(): WebSocket {
  const context = useContext(WSStateContext);

  if (context == undefined) {
    throw new Error('useWS must be used within a WSProvider');
  }

  return context;
}

export { WSProvider, useWS };
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68263036

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档