From 397b9d423ee6bd699c3c488c237decc67e10c4a6 Mon Sep 17 00:00:00 2001 From: Jordan Frankfurt Date: Thu, 22 Sep 2022 13:06:31 -0500 Subject: [PATCH] feat(explore): rewrite top tokens hook (#4697) rewrite top tokens hook --- .../Tokens/TokenTable/TokenTable.tsx | 31 ++----- src/graphql/data/Token.ts | 1 - src/graphql/data/TopTokens.ts | 88 ++++++++++--------- 3 files changed, 54 insertions(+), 66 deletions(-) diff --git a/src/components/Tokens/TokenTable/TokenTable.tsx b/src/components/Tokens/TokenTable/TokenTable.tsx index 58519c733c..1ae0dd052b 100644 --- a/src/components/Tokens/TokenTable/TokenTable.tsx +++ b/src/components/Tokens/TokenTable/TokenTable.tsx @@ -116,28 +116,15 @@ export default function TokenTable() { - {tokens.map((token, index) => { - if (tokens.length === index + 1) { - return ( - - ) - } else { - return ( - - ) - } - })} + {tokens.map((token, index) => ( + + ))} {loading && LoadingMoreRows} diff --git a/src/graphql/data/Token.ts b/src/graphql/data/Token.ts index f6ac3ec033..d4448895f5 100644 --- a/src/graphql/data/Token.ts +++ b/src/graphql/data/Token.ts @@ -113,7 +113,6 @@ const tokenQuery = graphql` ` export function useTokenQuery(address: string, chain: Chain, timePeriod: TimePeriod) { - //const cachedTopToken = cachedTopTokens[address] const data = useLazyLoadQuery(tokenQuery, { contract: { address, chain }, duration: toHistoryDuration(timePeriod), diff --git a/src/graphql/data/TopTokens.ts b/src/graphql/data/TopTokens.ts index dbf4ee1536..bdda7989ce 100644 --- a/src/graphql/data/TopTokens.ts +++ b/src/graphql/data/TopTokens.ts @@ -18,9 +18,8 @@ import { toHistoryDuration, useCurrentChainName } from './util' export function usePrefetchTopTokens() { const duration = toHistoryDuration(useAtomValue(filterTimeAtom)) const chain = useCurrentChainName() - - const top100 = useLazyLoadQuery(topTokens100Query, { duration, chain }) - return top100 + const args = useMemo(() => ({ chain, duration }), [chain, duration]) + return useLazyLoadQuery(topTokens100Query, args) } const topTokens100Query = graphql` @@ -131,59 +130,62 @@ function toContractInput(token: PrefetchedTopToken) { } export type TopToken = NonNullable[number] -export function useTopTokens(prefetchedData: TopTokens100Query['response']) { +interface UseTopTokensReturnValue { + loading: boolean + tokens: TopToken[] + loadMoreTokens: () => void +} +export function useTopTokens(prefetchedData: TopTokens100Query['response']): UseTopTokensReturnValue { const duration = toHistoryDuration(useAtomValue(filterTimeAtom)) const environment = useRelayEnvironment() - const [tokens, setTokens] = useState() + const [tokens, setTokens] = useState([]) - const [page, setPage] = useState(1) - const prefetchedSelectedTokens = useFilteredTokens(useSortedTokens(prefetchedData.topTokens)) + const [page, setPage] = useState(0) const [loading, setLoading] = useState(true) + const appendTokens = useCallback( + (newTokens: TopToken[]) => { + setTokens( + Object.values( + tokens + .concat(newTokens) + .reduce((acc, token) => (token?.address ? { ...acc, [token.address]: token } : acc), {}) + ) + ) + }, + [tokens] + ) + const loadMoreTokens = useCallback(() => setPage(page + 1), [page]) + // TopTokens should ideally be fetched with usePaginationFragment. The backend does not current support graphql cursors; // in the meantime, fetchQuery is used, as other relay hooks do not allow the refreshing and lazy loading we need - const loadTokens = useCallback( - (contracts: ContractInput[], onSuccess: (data: TopTokens_TokensQuery['response'] | undefined) => void) => { - fetchQuery( - environment, - tokensQuery, - { contracts, duration }, - { fetchPolicy: 'store-or-network' } - ) - .toPromise() - .then(onSuccess) - }, - [duration, environment] + const prefetchedSelectedTokens = useFilteredTokens(useSortedTokens(prefetchedData.topTokens)) + const contracts: ContractInput[] = useMemo( + () => prefetchedSelectedTokens.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE).map(toContractInput), + [page, prefetchedSelectedTokens] ) - const loadMoreTokens = useCallback(() => { - setLoading(true) - const contracts = prefetchedSelectedTokens.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE).map(toContractInput) - loadTokens(contracts, (data) => { - if (data?.tokens) { - setTokens([...(tokens ?? []), ...data.tokens]) - setLoading(false) - setPage(page + 1) - } - }) - }, [loadTokens, page, prefetchedSelectedTokens, tokens]) - - // Reset count when filters are changed useEffect(() => { - setLoading(true) - setTokens([]) - const contracts = prefetchedSelectedTokens.slice(0, PAGE_SIZE).map(toContractInput) - loadTokens(contracts, (data) => { - if (data?.tokens) { - // @ts-ignore prevent typescript from complaining about readonly data - setTokens(data.tokens) + const subscription = fetchQuery( + environment, + tokensQuery, + { contracts, duration }, + { fetchPolicy: 'store-or-network' } + ).subscribe({ + start() { + setLoading(true) + }, + complete() { setLoading(false) - setPage(1) - } + }, + next(data) { + appendTokens(data.tokens as TopToken[]) + }, }) - }, [loadTokens, prefetchedSelectedTokens]) + return subscription.unsubscribe + }, [appendTokens, contracts, duration, environment]) - return { loading, tokens, loadMoreTokens } + return { loading, tokens: useFilteredTokens(useSortedTokens(tokens)) as TopToken[], loadMoreTokens } } export const tokensQuery = graphql`