From 5659d16afe0ed195caa05d6c523921e46f9607d6 Mon Sep 17 00:00:00 2001 From: chriskari Date: Mon, 28 Oct 2024 18:29:51 +0100 Subject: [PATCH] fix: introduced custom hook with proper error handling for polling --- src/shared/hooks/BackendAPI/useGet.js | 15 ++++---- src/shared/hooks/BackendAPI/usePolling.ts | 44 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/shared/hooks/BackendAPI/usePolling.ts diff --git a/src/shared/hooks/BackendAPI/useGet.js b/src/shared/hooks/BackendAPI/useGet.js index 8c59ab065b..b26d9cbd37 100644 --- a/src/shared/hooks/BackendAPI/useGet.js +++ b/src/shared/hooks/BackendAPI/useGet.js @@ -7,6 +7,7 @@ import { v4 as uuid } from 'uuid'; import { useFetch } from 'shared/hooks/BackendAPI/useFetch'; import { authDataState } from '../../../state/authDataAtom'; +import { usePolling } from './usePolling'; // allow consecutive requests to fail before displaying error const ERROR_TOLERANCY = 2; @@ -96,14 +97,12 @@ const useGetHook = processDataFn => [fetch, authData], ); - useEffect(() => { - const receivedForbidden = error?.code === 403; - - // POLLING - if (!pollingInterval || receivedForbidden || skip) return; - const intervalId = setInterval(refetch(true, data), pollingInterval); - return _ => clearInterval(intervalId); - }, [path, pollingInterval, data, error, skip, refetch]); + usePolling(() => refetch(true, data), { + pollingInterval, + error, + skip, + path, + }); useEffect(() => { // INITIAL FETCH on path being set/changed diff --git a/src/shared/hooks/BackendAPI/usePolling.ts b/src/shared/hooks/BackendAPI/usePolling.ts new file mode 100644 index 0000000000..e00b5976e2 --- /dev/null +++ b/src/shared/hooks/BackendAPI/usePolling.ts @@ -0,0 +1,44 @@ +import { useEffect, useCallback, useRef } from 'react'; + +interface PollingOptions { + pollingInterval?: number; + error?: any; + skip?: boolean; + path?: string; +} + +export const usePolling = ( + refetchFn: () => Promise, + options: PollingOptions, +) => { + const { pollingInterval = 0, error, skip = false, path } = options; + const intervalIdRef = useRef(); + + const cleanup = useCallback(() => { + if (intervalIdRef.current) { + clearInterval(intervalIdRef.current); + intervalIdRef.current = undefined; + } + }, []); + + const safeRefetch = useCallback(async () => { + try { + await refetchFn(); + } catch (error) { + cleanup(); + } + }, [refetchFn, cleanup]); + + useEffect(() => { + const receivedForbidden = error?.code === 403; + if (!pollingInterval || receivedForbidden || skip) { + cleanup(); + return; + } + + safeRefetch(); + intervalIdRef.current = setInterval(safeRefetch, pollingInterval); + + return cleanup; + }, [pollingInterval, error, skip, safeRefetch, cleanup, path]); +};