partysocket
Version:
A better WebSocket that Just Works™
167 lines (165 loc) • 5.32 kB
JavaScript
const require_ws = require("./ws.cjs");
let react = require("react");
//#region src/use-handlers.ts
/** Attaches event handlers to a WebSocket in a React Lifecycle-friendly way */
const useAttachWebSocketEventHandlers = (socket, options) => {
const handlersRef = (0, react.useRef)(options);
handlersRef.current = options;
(0, react.useEffect)(() => {
const onOpen = (event) => handlersRef.current?.onOpen?.(event);
const onMessage = (event) => handlersRef.current?.onMessage?.(event);
const onClose = (event) => handlersRef.current?.onClose?.(event);
const onError = (event) => handlersRef.current?.onError?.(event);
socket.addEventListener("open", onOpen);
socket.addEventListener("close", onClose);
socket.addEventListener("error", onError);
socket.addEventListener("message", onMessage);
return () => {
socket.removeEventListener("open", onOpen);
socket.removeEventListener("close", onClose);
socket.removeEventListener("error", onError);
socket.removeEventListener("message", onMessage);
};
}, [socket]);
};
//#endregion
//#region src/use-socket.ts
/** When any of the option values are changed, we should reinitialize the socket */
const getOptionsThatShouldCauseRestartWhenChanged = (options) => [
options.startClosed,
options.minUptime,
options.maxRetries,
options.connectionTimeout,
options.maxEnqueuedMessages,
options.maxReconnectionDelay,
options.minReconnectionDelay,
options.reconnectionDelayGrowFactor,
options.debug
];
/**
* Initializes a PartySocket (or WebSocket) and keeps it stable across renders,
* but reconnects and updates the reference when any of the connection args change.
*/
function useStableSocket({
options,
createSocket,
createSocketMemoKey: createOptionsMemoKey
}) {
const { enabled = true } = options;
const socketOptions = (0, react.useMemo)(() => {
return options;
}, [createOptionsMemoKey(options)]);
const [socket, setSocket] = (0, react.useState)(() =>
createSocket({
...socketOptions,
startClosed: true
})
);
const socketInitializedRef = (0, react.useRef)(null);
const createSocketRef = (0, react.useRef)(createSocket);
createSocketRef.current = createSocket;
const prevEnabledRef = (0, react.useRef)(enabled);
const prevSocketOptionsRef = (0, react.useRef)(socketOptions);
const optionsChangedWhileDisabledRef = (0, react.useRef)(false);
(0, react.useEffect)(() => {
const optionsChanged = prevSocketOptionsRef.current !== socketOptions;
prevSocketOptionsRef.current = socketOptions;
if (!enabled) {
socket.close();
prevEnabledRef.current = enabled;
if (optionsChanged) optionsChangedWhileDisabledRef.current = true;
return () => {
socket.close();
};
}
if (!prevEnabledRef.current && enabled) {
prevEnabledRef.current = enabled;
const needsNewSocket =
optionsChanged || optionsChangedWhileDisabledRef.current;
optionsChangedWhileDisabledRef.current = false;
if (!needsNewSocket) {
socket.reconnect();
return () => {
socket.close();
};
}
const newSocket = createSocketRef.current({
...socketOptions,
startClosed: true
});
setSocket(newSocket);
return () => {
newSocket.close();
};
}
prevEnabledRef.current = enabled;
if (socketInitializedRef.current === socket)
if (optionsChanged) {
const newSocket = createSocketRef.current({
...socketOptions,
startClosed: true
});
setSocket(newSocket);
return () => {
newSocket.close();
};
} else {
if (socketOptions.startClosed !== true) socket.reconnect();
return () => {
socket.close();
};
}
else {
if (!socketInitializedRef.current) {
if (socketOptions.startClosed !== true) socket.reconnect();
} else if (socketInitializedRef.current !== socket) socket.reconnect();
socketInitializedRef.current = socket;
return () => {
socket.close();
};
}
}, [socket, socketOptions, enabled]);
return socket;
}
//#endregion
//#region src/use-ws.ts
function useWebSocket(url, protocols, options = {}) {
const socket = useStableSocket({
options,
createSocket: (options) => new require_ws.default(url, protocols, options),
createSocketMemoKey: (options) =>
JSON.stringify([
url,
protocols,
...getOptionsThatShouldCauseRestartWhenChanged(options)
])
});
useAttachWebSocketEventHandlers(socket, options);
return socket;
}
//#endregion
Object.defineProperty(exports, "getOptionsThatShouldCauseRestartWhenChanged", {
enumerable: true,
get: function () {
return getOptionsThatShouldCauseRestartWhenChanged;
}
});
Object.defineProperty(exports, "useAttachWebSocketEventHandlers", {
enumerable: true,
get: function () {
return useAttachWebSocketEventHandlers;
}
});
Object.defineProperty(exports, "useStableSocket", {
enumerable: true,
get: function () {
return useStableSocket;
}
});
Object.defineProperty(exports, "useWebSocket", {
enumerable: true,
get: function () {
return useWebSocket;
}
});
//# sourceMappingURL=use-ws-CpH04vIA.cjs.map