Updates
This commit is contained in:
@@ -1,31 +1,42 @@
|
||||
import React from "react";
|
||||
|
||||
type Param = {
|
||||
elementRef?: React.MutableRefObject<Element | undefined>;
|
||||
elementRef?: React.RefObject<Element | undefined>;
|
||||
className?: string;
|
||||
elId?: string;
|
||||
options?: IntersectionObserverInit;
|
||||
removeIntersected?: boolean;
|
||||
delay?: number;
|
||||
};
|
||||
|
||||
let timeout: any;
|
||||
|
||||
export default function useIntersectionObserver({
|
||||
elementRef,
|
||||
className,
|
||||
options,
|
||||
removeIntersected,
|
||||
delay,
|
||||
elId,
|
||||
}: Param) {
|
||||
const [isIntersecting, setIsIntersecting] = React.useState(false);
|
||||
const [refresh, setRefresh] = React.useState(0);
|
||||
|
||||
const observerTriggerDelay = delay || 200;
|
||||
|
||||
const observerCallback: IntersectionObserverCallback = React.useCallback(
|
||||
(entries, observer) => {
|
||||
const entry = entries[0];
|
||||
window.clearTimeout(timeout);
|
||||
|
||||
if (entry.isIntersecting) {
|
||||
setIsIntersecting(true);
|
||||
timeout = setTimeout(() => {
|
||||
setIsIntersecting(true);
|
||||
|
||||
if (removeIntersected) {
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
if (removeIntersected) {
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
}, observerTriggerDelay);
|
||||
} else {
|
||||
setIsIntersecting(false);
|
||||
}
|
||||
@@ -34,7 +45,9 @@ export default function useIntersectionObserver({
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const element = elementRef?.current;
|
||||
const element = elId
|
||||
? document.getElementById(elId)
|
||||
: elementRef?.current;
|
||||
const elements = className
|
||||
? document.querySelectorAll(`.${className}`)
|
||||
: null;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
|
||||
type Params = {
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
let timeout: any;
|
||||
|
||||
export default function twuiUseReady(params?: Params) {
|
||||
const [ready, setReady] = React.useState(false);
|
||||
|
||||
const finalTimeout = params?.timeout || 300;
|
||||
|
||||
React.useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
window.clearTimeout(timeout);
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
setReady(true);
|
||||
}, finalTimeout);
|
||||
});
|
||||
|
||||
return function () {
|
||||
window.clearTimeout(timeout);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { ready };
|
||||
}
|
||||
@@ -4,12 +4,10 @@ export type UseWebsocketHookParams = {
|
||||
debounce?: number;
|
||||
url: string;
|
||||
disableReconnect?: boolean;
|
||||
keepAliveDuration?: number;
|
||||
refreshConnection?: number;
|
||||
};
|
||||
|
||||
let reconnectInterval: any;
|
||||
let msgInterval: any;
|
||||
let sendInterval: any;
|
||||
|
||||
export const WebSocketEventNames = ["wsDataEvent", "wsMessageEvent"] as const;
|
||||
|
||||
let tries = 0;
|
||||
@@ -28,8 +26,25 @@ let tries = 0;
|
||||
*/
|
||||
export default function useWebSocket<
|
||||
T extends { [key: string]: any } = { [key: string]: any }
|
||||
>({ url, debounce, disableReconnect }: UseWebsocketHookParams) {
|
||||
>({
|
||||
url,
|
||||
debounce,
|
||||
disableReconnect,
|
||||
keepAliveDuration,
|
||||
refreshConnection,
|
||||
}: UseWebsocketHookParams) {
|
||||
const DEBOUNCE = debounce || 200;
|
||||
const KEEP_ALIVE_DURATION = keepAliveDuration || 1000 * 30;
|
||||
const KEEP_ALIVE_TIMEOUT = 1000 * 60 * 3;
|
||||
|
||||
const KEEP_ALIVE_MESSAGE = "twui::ping";
|
||||
|
||||
let uptime = 0;
|
||||
|
||||
let reconnectInterval: any;
|
||||
let msgInterval: any;
|
||||
let sendInterval: any;
|
||||
let keepAliveInterval: any;
|
||||
|
||||
const [socket, setSocket] = React.useState<WebSocket | undefined>(
|
||||
undefined
|
||||
@@ -38,6 +53,9 @@ export default function useWebSocket<
|
||||
const messageQueueRef = React.useRef<string[]>([]);
|
||||
const sendMessageQueueRef = React.useRef<string[]>([]);
|
||||
|
||||
/**
|
||||
* # Dispatch Custom Event
|
||||
*/
|
||||
const dispatchCustomEvent = React.useCallback(
|
||||
(evtName: (typeof WebSocketEventNames)[number], value: string | T) => {
|
||||
const event = new CustomEvent(evtName, {
|
||||
@@ -51,6 +69,9 @@ export default function useWebSocket<
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* # Connect to Websocket
|
||||
*/
|
||||
const connect = React.useCallback(() => {
|
||||
const wsURL = url;
|
||||
if (!wsURL) return;
|
||||
@@ -59,22 +80,41 @@ export default function useWebSocket<
|
||||
|
||||
ws.onopen = (ev) => {
|
||||
window.clearInterval(reconnectInterval);
|
||||
window.clearInterval(keepAliveInterval);
|
||||
keepAliveInterval = setInterval(() => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(KEEP_ALIVE_MESSAGE);
|
||||
uptime += KEEP_ALIVE_DURATION;
|
||||
if (uptime >= KEEP_ALIVE_TIMEOUT) {
|
||||
console.log("Websocket connection timed out ...");
|
||||
window.clearInterval(keepAliveInterval);
|
||||
ws.close();
|
||||
}
|
||||
}
|
||||
}, KEEP_ALIVE_DURATION);
|
||||
setSocket(ws);
|
||||
tries = 0;
|
||||
console.log(`Websocket connected to ${wsURL}`);
|
||||
uptime = 0;
|
||||
};
|
||||
|
||||
ws.onmessage = (ev) => {
|
||||
window.clearInterval(msgInterval);
|
||||
messageQueueRef.current.push(ev.data);
|
||||
msgInterval = setInterval(handleReceivedMessageQueue, DEBOUNCE);
|
||||
if (ev.data !== KEEP_ALIVE_MESSAGE) {
|
||||
uptime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = (ev) => {
|
||||
console.log("Websocket closed!");
|
||||
|
||||
if (disableReconnect) return;
|
||||
|
||||
console.log("Websocket closed ... Attempting to reconnect ...");
|
||||
console.log("Attempting to reconnect ...");
|
||||
console.log("URL:", url);
|
||||
window.clearInterval(keepAliveInterval);
|
||||
|
||||
reconnectInterval = setInterval(() => {
|
||||
if (tries >= 3) {
|
||||
@@ -89,6 +129,31 @@ export default function useWebSocket<
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* # Window Close Handler
|
||||
*/
|
||||
const handleWindowClose = React.useCallback(() => {
|
||||
console.log("Window Unloaded ...");
|
||||
}, [socket]);
|
||||
|
||||
/**
|
||||
* # Window Focus Handler
|
||||
*/
|
||||
const handleWindowFocus = React.useCallback(() => {
|
||||
if (socket?.readyState === WebSocket.CLOSED) {
|
||||
console.log("Websocket closed ... Attempting to reconnect ...");
|
||||
connect();
|
||||
}
|
||||
if (socket?.readyState === WebSocket.OPEN) {
|
||||
console.log("Websocket connection alive ...");
|
||||
socket.send(KEEP_ALIVE_MESSAGE);
|
||||
uptime = 0;
|
||||
}
|
||||
}, [socket]);
|
||||
|
||||
/**
|
||||
* # Initial Connection
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
connect();
|
||||
|
||||
@@ -97,6 +162,36 @@ export default function useWebSocket<
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* # Window Close and Focus Handlers
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
if (!socket) return;
|
||||
|
||||
window.addEventListener("beforeunload", handleWindowClose, {
|
||||
once: true,
|
||||
});
|
||||
window.addEventListener("focus", handleWindowFocus);
|
||||
|
||||
return function () {
|
||||
window.removeEventListener("focus", handleWindowFocus);
|
||||
window.removeEventListener("beforeunload", handleWindowClose);
|
||||
};
|
||||
}, [socket]);
|
||||
|
||||
/**
|
||||
* # Refresh Connection
|
||||
*/
|
||||
React.useEffect(() => {
|
||||
console.log("Refreshing connection ...");
|
||||
|
||||
if (!socket) return;
|
||||
if (socket.readyState !== WebSocket.CLOSED) {
|
||||
socket?.close();
|
||||
}
|
||||
connect();
|
||||
}, [refreshConnection]);
|
||||
|
||||
/**
|
||||
* Received Message Queue Handler
|
||||
*/
|
||||
@@ -113,6 +208,7 @@ export default function useWebSocket<
|
||||
}
|
||||
} else {
|
||||
window.clearInterval(msgInterval);
|
||||
uptime = 0;
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user