There are multiple things that need to be done in order for ws to work on Next.js.
Firstly, it is important to realize where do I want my ws code to run. React code on Next.js runs in two environments: On the server (when building the page or when using ssr) and on the client.
Making a ws connection at page build time has little utility, thats why I will cover client-side ws only.
The global Websocket class is a browser only feature and is not present on the server. Thats why we need to prevent any instantiation until the code is run in the browser. One simple way to do so would be:
export const isBrowser = typeof window !== "undefined";
export const wsInstance = isBrowser ? new Websocket(...) : null;
Also, you do not need to use react context for holding the instance, it is perfectly possible to keep it at global scope and import the instance, unless you wish to open the connection lazily.
If you still decide to use react context (or initialize the ws client anywhere in the react tree), it is important to memoize the instance, so that it isn't created on every update of your react node.
const wsInstance = useMemo(() => isBrowser ? new Websocket(...) : null, []);
or
const [wsInstance] = useState(() => isBrowser ? new Websocket(...) : null);
Any event handler registrations created in react should be wrapped inside a useEffect with a return function which removes the event listener, this is to prevent memory leaks, also, a dependency array should be specified. If your component is unmounted, the useEffect hook will remove the event listener as well.
If you wish to reinitailize the ws and dispose of the current connection, then it is possible to do something similar to the following:
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(...)
}
}, [])