A hook to use socket in a more linear and reactive way
// /contexts/chat.js
import { useState, useEffect, useCallback, useRef } from "react";
import socketIOClient from "socket.io-client";
import { createContext, useContextSelector } from "use-context-selector";
const socketBaseURL = "...";
const SocketContext = createContext();
export default function SocketProvider({ children }) {
const user = "USER_ID_FROM_GLOBAL_STATE";
const [isConnected, setConnected] = useState(false);
const [isActive, setActive] = useState(false);
const socketRef = useRef(null);
useEffect(() => {
if (!socketRef.current) {
const token = "AUTH_TOKEN";
socketRef.current = socketIOClient(socketBaseURL, {
transports: ["websocket"],
query: { token },
});
socketRef.current.on("connect", () => setConnected(true));
socketRef.current.on("disconnect", () => setConnected(false));
socketRef.current.on("SOME_EVENT", (message) => {
// do stuff
});
socketRef.current.on("error", () => {});
}
return () => {
if (socketRef.current) resetConnection();
};
}, []);
const resetConnection = () => {
if (socketRef.current) socketRef.current.disconnect();
socketRef.current = null;
};
const contextValue = {
isConnected,
isActive,
// some other methods
resetConnection,
ref: socketRef,
};
return (
<SocketContext.Provider value={contextValue}>
{children}
</SocketContext.Provider>
);
}
Instead of doing this in every file:
import { useContextSelector } from "use-context-selector";
import SocketContext from "@contexts/chat";
function Component(props) {
const chat = useContextSelector(SocketContext, ...);
}
You can create a hook that does this and you just need to import that one hook in your files:
import { useContextSelector } from "use-context-selector";
import SocketContext from "@context/Socket";
const useSocket = (selector) => {
const context = useContext(SocketContext, selector);
if (isDevelopment && context === undefined)
throw new Error("useSocket must be used within a SocketProvider");
return context;
};
export default useSocket;
This doesn't do anything right now, it's just a way to skip multiple imports and also it gives us some flexibility in the future and makes our code future proof.
const Chat = function (props) {
const socket = useSocket((state) => state.isActive);
return <div>...</div>;
};