UNPKG

@supunlakmal/hooks

Version:

A collection of reusable React hooks

133 lines 5.82 kB
import { useState, useEffect, useRef, useCallback } from 'react'; import { useNetworkState } from './useNetworkState'; // Assuming signature: () => { online: boolean } const isBrowser = typeof window !== 'undefined'; /** * Manages a WebSocket connection that is only active when the user is online. * * @param url The WebSocket server URL. Pass null or undefined to disconnect. * @param options Optional configuration for event handlers and retry logic. * @returns An object with connection status, last message, error, send function, and the WebSocket instance. */ export function useNetworkAwareWebSocket(url, options = {}) { const { onOpen, onClose, onError, onMessage, retryOnError = false, retryDelay = 2000, // Default 2 seconds retry delay } = options; const [isConnected, setIsConnected] = useState(false); const [lastMessage, setLastMessage] = useState(null); const [error, setError] = useState(null); const wsRef = useRef(null); const retryTimeoutRef = useRef(null); const explicitCloseRef = useRef(false); // Track if closed manually or due to error/offline const { online } = useNetworkState(); const clearRetryTimeout = () => { if (retryTimeoutRef.current) { clearTimeout(retryTimeoutRef.current); retryTimeoutRef.current = null; } }; const connectWebSocket = useCallback(() => { if (!url || !isBrowser || wsRef.current) { return; // Don't connect if no URL, not in browser, or already connected } console.log('Attempting WebSocket connection to:', url); explicitCloseRef.current = false; clearRetryTimeout(); // Clear any pending retries const ws = new WebSocket(url); wsRef.current = ws; ws.onopen = (event) => { console.log('WebSocket opened:', url); setIsConnected(true); setError(null); onOpen === null || onOpen === void 0 ? void 0 : onOpen(event); }; ws.onmessage = (event) => { setLastMessage(event); onMessage === null || onMessage === void 0 ? void 0 : onMessage(event); }; ws.onerror = (event) => { console.error('WebSocket error:', event); setError(event); wsRef.current = null; // Ensure ref is cleared on error setIsConnected(false); // Explicitly set disconnected on error onError === null || onError === void 0 ? void 0 : onError(event); // Retry logic if (!explicitCloseRef.current && retryOnError && online) { console.log(`WebSocket attempting retry in ${retryDelay}ms...`); clearRetryTimeout(); // Ensure no double timeouts retryTimeoutRef.current = setTimeout(connectWebSocket, retryDelay); } }; ws.onclose = (event) => { console.log('WebSocket closed:', url, 'Clean close:', event.wasClean); // Only update state if it wasn't an explicit close initiated by the hook logic if (wsRef.current === ws) { // Ensure this close event belongs to the current ref instance wsRef.current = null; setIsConnected(false); if (!explicitCloseRef.current) { setError(null); // Clear error only if not an explicit close/error handled by onError } onClose === null || onClose === void 0 ? void 0 : onClose(event); // Retry logic (also on unclean close if retrying enabled) if (!event.wasClean && !explicitCloseRef.current && retryOnError && online) { console.log(`WebSocket attempting retry (unclean close) in ${retryDelay}ms...`); clearRetryTimeout(); retryTimeoutRef.current = setTimeout(connectWebSocket, retryDelay); } } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [ url, retryOnError, retryDelay, onOpen, onClose, onError, onMessage, online, ]); // Reconnect if URL/options/network change const disconnectWebSocket = useCallback(() => { clearRetryTimeout(); explicitCloseRef.current = true; // Mark as intentional close if (wsRef.current) { console.log('Disconnecting WebSocket explicitly:', url); wsRef.current.close(1000, 'Hook cleanup or explicit disconnect'); // 1000: Normal Closure // wsRef.current will be set to null in the onclose handler } }, [url]); // Effect to manage connection based on network state and URL useEffect(() => { if (online && url) { // If online and URL is set, try connecting (connectWebSocket handles existing connections) connectWebSocket(); } else { // If offline or no URL, disconnect disconnectWebSocket(); } // Cleanup function on unmount or if dependencies change return () => { disconnectWebSocket(); }; }, [online, url, connectWebSocket, disconnectWebSocket]); const sendMessage = useCallback((data) => { if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) { wsRef.current.send(data); } else { console.warn('WebSocket not connected. Cannot send message.'); } }, [] // Depends only on the ref, which is stable ); return { lastMessage, isConnected, error, sendMessage, websocket: wsRef.current, }; } //# sourceMappingURL=useNetworkAwareWebSocket.js.map