Skip to content

Instantly share code, notes, and snippets.

@Klerith
Created January 13, 2026 14:51
Show Gist options
  • Select an option

  • Save Klerith/d7b3dd8b0e880d79ce84a4efa44a6d09 to your computer and use it in GitHub Desktop.

Select an option

Save Klerith/d7b3dd8b0e880d79ce84a4efa44a6d09 to your computer and use it in GitHub Desktop.
WebSocket context creado en el curso
import {
createContext,
useCallback,
useEffect,
useRef,
useState,
type ReactNode,
} from 'react';
import type { Client, LatLng } from '../types';
import Cookies from 'js-cookie';
type ConnectionStatus =
| 'offline'
| 'connecting'
| 'connected'
| 'disconnected'
| 'error';
// Tipados específicos
export type SocketMessage =
| {
type: 'CLIENT_REGISTER';
payload: { name: string; color: string; coords: LatLng };
}
| { type: 'CLIENT_MOVE'; payload: { clientId: string; coords: LatLng } }
| { type: 'GET_CLIENTS' };
export type SocketResponse =
| { type: 'ERROR'; payload: { message: string } }
| { type: 'WELCOME'; payload: Client }
| { type: 'CLIENTS_STATE'; payload: Client[] }
| { type: 'CLIENT_JOINED'; payload: Client }
| { type: 'CLIENT_MOVED'; payload: Client }
| { type: 'CLIENT_LEFT'; payload: { clientId: string } };
export type SocketMessageListener = (message: SocketResponse) => void;
interface WebSocketContextState {
status: ConnectionStatus;
socketId: string | null;
// Methods
connectToServer: (name: string, color: string, latLng: LatLng) => void;
disconnect: () => void;
send: (message: SocketMessage) => void;
subscribeToMessages: (listener: SocketMessageListener) => () => void;
}
// eslint-disable-next-line react-refresh/only-export-components
export const WebSocketContext = createContext({} as WebSocketContextState);
const messageListenersRef = new Set<SocketMessageListener>();
let connecting = false;
interface Props {
children: ReactNode;
url: string;
}
export const WebSocketProvider = ({ children, url }: Props) => {
const [status, setStatus] = useState<ConnectionStatus>('offline');
const [socketId, setSocketId] = useState<string | null>(null);
const socket = useRef<WebSocket | null>(null);
const shouldReconnectRef = useRef(true);
useEffect(() => {
console.log({ status });
}, [status]);
const disconnect = () => {
socket.current?.close();
socket.current = null;
shouldReconnectRef.current = false;
setStatus('offline');
};
const connect = useCallback(() => {
if (connecting) return;
connecting = true;
setStatus('connecting');
const ws = new WebSocket(url);
shouldReconnectRef.current = true;
ws.addEventListener('open', () => {
connecting = false;
socket.current = ws;
setStatus('connected');
});
ws.addEventListener('close', () => {
socket.current = null;
setStatus('disconnected');
});
ws.addEventListener('error', (event) => {
console.log({ customError: event });
});
ws.addEventListener('message', (event) => {
try {
const message = JSON.parse(event.data);
if (message.type === 'WELCOME') {
setSocketId(message.payload.clientId);
}
// TODO: aquí tenemos que emitir los mensajes
messageListenersRef.forEach((listener) => listener(message));
} catch (error) {
console.error('Invalid socket message', error);
}
});
return ws;
}, [url]);
const connectToServer = (name: string, color: string, latLng: LatLng) => {
if (status === 'connecting' || status === 'connected') return;
Cookies.set('name', name);
Cookies.set('color', color);
Cookies.set('coords', JSON.stringify(latLng));
connect();
};
const subscribeToMessages = (listener: SocketMessageListener) => {
messageListenersRef.add(listener);
return () => {
messageListenersRef.delete(listener);
};
};
// useEffect(() => {
// const ws = connect();
// return () => {
// if (ws.readyState === WebSocket.OPEN) {
// ws.close();
// }
// };
// }, [connect]);
// Función básica de re-conexión
useEffect(() => {
if (!shouldReconnectRef.current) return;
let interval: ReturnType<typeof setInterval>;
if (status === 'disconnected') {
interval = setInterval(() => {
console.log('Reconnecting every 1 second...');
connect();
}, 1000);
}
return () => {
if (interval) {
clearInterval(interval);
}
};
}, [status, connect]);
const send = (message: SocketMessage) => {
if (!socket.current) throw new Error('Socket not connected');
const jsonMessage = JSON.stringify(message);
socket.current?.send(jsonMessage);
};
return (
<WebSocketContext
value={{
status: status,
send: send,
connectToServer: connectToServer,
disconnect: disconnect,
socketId: socketId,
subscribeToMessages: subscribeToMessages,
}}
>
{children}
</WebSocketContext>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment