UNPKG

react-eventsource-hook

Version:

React hook for the EventSource interface (Server-Sent Events) with retry backoff, pause on hidden, and reconnect/close controls.

169 lines (167 loc) 5.54 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { useEventSource: () => useEventSource }); module.exports = __toCommonJS(index_exports); // src/use-eventsource.ts var import_react = require("react"); var import_fetch_event_source = require("@microsoft/fetch-event-source"); function useEventSource(options) { const { url, withCredentials = false, init, fetch: customFetch, retryIntervalMs, maxRetryIntervalMs, pauseOnHidden = true, onopen: onOpen, onmessage: onMessage, onerror: onError, onclose: onClose } = options; const [readyState, setReadyState] = (0, import_react.useState)(0); const [lastEventId, setLastEventId] = (0, import_react.useState)(""); const [error, setError] = (0, import_react.useState)(); const [events, setEvents] = (0, import_react.useState)([]); const controllerRef = (0, import_react.useRef)(null); const retryCountRef = (0, import_react.useRef)(0); const onOpenRef = (0, import_react.useRef)(void 0); const onMessageRef = (0, import_react.useRef)(void 0); const onErrorRef = (0, import_react.useRef)(void 0); const onCloseRef = (0, import_react.useRef)(void 0); (0, import_react.useEffect)(() => { onOpenRef.current = onOpen; }, [onOpen]); (0, import_react.useEffect)(() => { onMessageRef.current = onMessage; }, [onMessage]); (0, import_react.useEffect)(() => { onErrorRef.current = onError; }, [onError]); (0, import_react.useEffect)(() => { onCloseRef.current = onClose; }, [onClose]); const start = (0, import_react.useCallback)(() => { var _a, _b; (_a = controllerRef.current) == null ? void 0 : _a.abort(); const controller = new AbortController(); controllerRef.current = controller; setReadyState(0); const requestInit = { ...init }; if (withCredentials && !requestInit.credentials) { requestInit.credentials = "include"; } const headersRecord = {}; const existing = new Headers((_b = requestInit.headers) != null ? _b : {}); existing.forEach((value, key) => { headersRecord[key] = value; }); if (lastEventId) { headersRecord["Last-Event-ID"] = lastEventId; } (0, import_fetch_event_source.fetchEventSource)(url, { ...requestInit, headers: headersRecord, signal: controller.signal, fetch: customFetch, async onopen(response) { var _a2; setReadyState(1); retryCountRef.current = 0; (_a2 = onOpenRef.current) == null ? void 0 : _a2.call(onOpenRef, response); }, onmessage(message) { var _a2, _b2; setLastEventId((_a2 = message.id) != null ? _a2 : ""); setEvents((prev) => [...prev, message]); (_b2 = onMessageRef.current) == null ? void 0 : _b2.call(onMessageRef, message); }, onerror(err) { var _a2; setError(err); (_a2 = onErrorRef.current) == null ? void 0 : _a2.call(onErrorRef, err); setReadyState(2); if (retryIntervalMs != null) { const delay = Math.min( retryIntervalMs * 2 ** retryCountRef.current, maxRetryIntervalMs != null ? maxRetryIntervalMs : Infinity ); retryCountRef.current += 1; setTimeout(() => { if (!controller.signal.aborted) start(); }, delay); } else { throw err; } }, onclose() { var _a2; setReadyState(2); (_a2 = onCloseRef.current) == null ? void 0 : _a2.call(onCloseRef); } }); }, [ url, init, customFetch, withCredentials, retryIntervalMs, maxRetryIntervalMs ]); (0, import_react.useEffect)(() => { start(); return () => { var _a; (_a = controllerRef.current) == null ? void 0 : _a.abort(); }; }, [start]); (0, import_react.useEffect)(() => { if (!pauseOnHidden) return; const handler = () => { var _a; if (document.hidden) { (_a = controllerRef.current) == null ? void 0 : _a.abort(); } else { start(); } }; document.addEventListener("visibilitychange", handler); return () => { document.removeEventListener("visibilitychange", handler); }; }, [pauseOnHidden, start]); const close = (0, import_react.useCallback)(() => { var _a; (_a = controllerRef.current) == null ? void 0 : _a.abort(); setReadyState(2); }, []); const reconnect = (0, import_react.useCallback)(() => { start(); }, [start]); return { readyState, lastEventId, error, events, close, reconnect }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { useEventSource }); //# sourceMappingURL=index.cjs.map