diff --git a/app/javascript/src/hooks/usePersistedState.ts b/app/javascript/src/hooks/usePersistedState.ts new file mode 100644 index 0000000..7765db7 --- /dev/null +++ b/app/javascript/src/hooks/usePersistedState.ts @@ -0,0 +1,58 @@ +import type { SetStateAction, Dispatch } from "react"; +import { useState, useEffect, useCallback } from "react"; + +export const usePersistedState = ( + key: string, + initialValue: S, + ttl?: number +): [S, Dispatch>] => { + type PersistedStatePaylaod = { + value: S; + expiry?: number; + }; + + const [state, setState] = useState(() => { + try { + const localStorageRaw = localStorage.getItem(key); + + if (localStorageRaw === null) return initialValue; + + const payload: PersistedStatePaylaod = JSON.parse(localStorageRaw); + + if (!payload.expiry) return payload.value; + + const expired = new Date().getTime() > payload.expiry; + + if (expired) return initialValue; + + return payload.value; + } catch { + return initialValue; + } + }); + + const setLocalStorage = useCallback( + (value) => { + try { + const expiry = ttl ? new Date().getTime() + ttl : undefined; + const payload: PersistedStatePaylaod = { value, expiry }; + + localStorage.setItem(key, JSON.stringify(payload)); + } catch { + localStorage.removeItem(key); + } + }, + [key] + ); + + const setLocalStorageAndState: Dispatch> = (newState) => { + setLocalStorage(newState); + setState(newState); + }; + + useEffect(() => { + setLocalStorage(state); + }, [setLocalStorage, state]); + + return [state, setLocalStorageAndState]; +}; diff --git a/app/javascript/src/pages/Home/PoolListing.tsx b/app/javascript/src/pages/Home/PoolListing.tsx index 7aa4240..e1764e1 100644 --- a/app/javascript/src/pages/Home/PoolListing.tsx +++ b/app/javascript/src/pages/Home/PoolListing.tsx @@ -12,10 +12,16 @@ import { getEndBlock } from "../../utils/getEndBlock"; import type { PoolListingQuery } from "./__generated__/PoolListingQuery.graphql"; import { notEmpty } from "../../utils/notEmpty"; import { Spinner } from "../../components"; +import { usePersistedState } from "../../hooks/usePersistedState"; export const PoolListing = () => { const { provider } = useBsc(); - const [validPools, setValidPools] = useState([]); + const [validPools, setValidPools] = usePersistedState( + "validPools", + [], + 1200000 // 20 minutes + ); + const [isLoadingPools, setIsLoadingPools] = useState(true); const { currentUser } = useLazyLoadQuery( @@ -35,6 +41,8 @@ export const PoolListing = () => { useEffect(() => { (async () => { + if (validPools.length) return; + const blockNumber = await provider.getBlockNumber(); const getChef = (pool: PoolConfig) => { @@ -67,9 +75,9 @@ export const PoolListing = () => { setValidPools(pools.filter(notEmpty)); }); })(); - }, [provider]); + }, [provider, setValidPools, validPools.length]); - if (isLoadingPools) { + if (isLoadingPools && !validPools.length) { return (