use-next-sse
Version:
A lightweight Server-Sent Events (SSE) library for Next.js, enabling real-time, unidirectional data streaming from server to client
104 lines (103 loc) • 5.16 kB
JavaScript
;
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.useSSE = void 0;
const react_1 = require("react");
const sse_manager_1 = require("./sse-manager");
/**
* Hook to manage Server-Sent Events (SSE) connections.
*
* @template T - The type of the data expected from the SSE.
* @param {SSEOptions} options - The options for the SSE connection. {@link SSEOptions}
* @param {string} options.url - The URL to connect to for SSE.
* @param {string} [options.eventName='message'] - The name of the event to listen for.
* @param {boolean | { interval?: number, maxAttempts?: number }} [options.reconnect] - Whether to automatically reconnect if the connection is lost. If an object, the interval and maxAttempts can be specified. Default `false`.
* @param {number} [options.reconnect.interval] - The interval in milliseconds to wait before reconnecting. Default `1000`ms.
* @param {number} [options.reconnect.maxAttempts] - The maximum number of reconnection attempts. Default `5`.
* @param {boolean} [options.withCredentials=false] - Whether to include credentials in the request. Default `false`.
* @returns {SSEResult<T>} The result of the SSE connection, including data, error, last event ID, and a close function. {@link SSEResult}
*
* @example
* const { data, error, lastEventId, close } = useSSE<{ message: string }>({ url: 'https://example.com/sse' });
*
* useEffect(() => {
* if (data) {
* console.log(data.message);
* }
* }, [data]);
*/
function useSSE({ url, eventName = 'message', reconnect = false, withCredentials = false, }) {
const [data, setData] = (0, react_1.useState)(null);
const [error, setError] = (0, react_1.useState)(null);
const [lastEventId, setLastEventId] = (0, react_1.useState)(null);
const [connectionState, setConnectionState] = (0, react_1.useState)('connecting');
const reconnectAttempts = (0, react_1.useRef)(0);
const reconnectTimeout = (0, react_1.useRef)(null);
const cleanupRef = (0, react_1.useRef)(() => { });
const close = (0, react_1.useCallback)(() => {
if (reconnectTimeout.current) {
clearTimeout(reconnectTimeout.current);
}
cleanupRef.current();
setConnectionState('closed');
}, [cleanupRef.current]);
(0, react_1.useEffect)(() => {
const connect = () => {
setConnectionState('connecting');
const source = sse_manager_1.sseManager.getConnection(url, { withCredentials });
const handleOpen = () => {
setConnectionState('open');
reconnectAttempts.current = 0;
};
const handleMessage = (event) => {
try {
const parsedData = JSON.parse(event.data);
setData(parsedData);
setLastEventId(event.lastEventId);
setError(null);
}
catch (err) {
setError(new Error('Failed to parse event data'));
}
};
const handleError = (event) => {
setConnectionState('closed');
const maxAttempts = typeof reconnect === 'object' && reconnect.maxAttempts ? reconnect.maxAttempts : 5;
if (reconnect && reconnectAttempts.current < maxAttempts) {
const interval = typeof reconnect === 'object' && reconnect.interval ? reconnect.interval : 1000;
reconnectAttempts.current += 1;
reconnectTimeout.current = window.setTimeout(() => {
sse_manager_1.sseManager.removeEventListener(url, eventName, handleMessage);
source.removeEventListener('open', handleOpen);
source.removeEventListener('error', handleError);
connect();
}, interval);
}
else {
destructor();
}
setError(new Error(`EventSource failed\n\nsource state:${source.readyState}\nCLOSED: ${source.CLOSED}\nCONNECTING: ${source.CONNECTING}\nOPEN: ${source.OPEN}`));
};
const destructor = () => {
sse_manager_1.sseManager.removeEventListener(url, eventName, handleMessage);
source.removeEventListener('open', handleOpen);
source.removeEventListener('error', handleError);
sse_manager_1.sseManager.releaseConnection(url);
};
sse_manager_1.sseManager.addEventListener(url, eventName, handleMessage);
source.addEventListener('open', handleOpen);
source.addEventListener('error', handleError);
return destructor;
};
const cleanup = connect();
cleanupRef.current = cleanup;
return () => {
if (reconnectTimeout.current) {
clearTimeout(reconnectTimeout.current);
}
cleanup();
};
}, [url, eventName, reconnect]);
return { data, error, lastEventId, close, connectionState };
}
exports.useSSE = useSSE;