UNPKG

@saberhq/sail

Version:

Account caching and batched loading for React-based Solana applications.

125 lines (113 loc) 3.23 kB
import type { Network } from "@saberhq/solana-contrib"; import type { ProgramAccount } from "@saberhq/token-utils"; import { useSolana } from "@saberhq/use-solana"; import type { PublicKey } from "@solana/web3.js"; import { useEffect } from "react"; import type { UseQueryOptions, UseQueryResult } from "react-query"; import { useQuery } from "react-query"; import type { FetchKeysFn } from ".."; import { fetchKeysMaybe, SailProgramAccountParseError, serializeKeys, useAccountsSubscribe, } from ".."; import { useSail } from "../provider"; import type { ProgramAccountParser } from "./programAccounts"; /** * Result of a parsed account query. */ export type BatchParsedAccountQueryResult<T> = UseQueryResult< BatchedParsedAccountQueryData<T> >; /** * Data resulting from a batch query. */ export type BatchedParsedAccountQueryKeys = | readonly (PublicKey | null | undefined)[] | null | undefined; /** * Data resulting from a batch query. */ export type BatchedParsedAccountQueryData<T> = | readonly (ProgramAccount<T> | null | undefined)[] | null | undefined; export const makeBatchedParsedAccountQuery = <T>( keys: BatchedParsedAccountQueryKeys, network: Network, fetchKeys: FetchKeysFn, parser: ProgramAccountParser<T>, options: Omit< UseQueryOptions<BatchedParsedAccountQueryData<T>>, "queryFn" | "queryKey" > = {} ): UseQueryOptions<BatchedParsedAccountQueryData<T>> => ({ queryKey: [ "sail/batchedParsedAccounts", network, ...(keys ? serializeKeys(keys) : keys === null ? ["null"] : ["undefined"]), ], queryFn: async (): Promise<BatchedParsedAccountQueryData<T>> => { if (!keys) { return keys; } const accountsData = await fetchKeysMaybe(fetchKeys, keys); return accountsData.map((result): ProgramAccount<T> | null | undefined => { if (!result) { return result; } const data = result.data; if (!data) { return null; } try { const parsed = parser.parse(data.accountInfo.data); return { publicKey: data.accountId, account: parsed, }; } catch (e) { throw new SailProgramAccountParseError(e, data, parser); } }); }, staleTime: Infinity, ...options, }); /** * Parses accounts with the given parser, fetching them in batch. * * @param keys * @param parser * @returns */ export const useBatchedParsedAccounts = <T>( keys: BatchedParsedAccountQueryKeys, parser: ProgramAccountParser<T>, options: Omit< UseQueryOptions<BatchedParsedAccountQueryData<T>>, "queryFn" | "queryKey" > = {} ): BatchParsedAccountQueryResult<T> => { const { fetchKeys, onBatchCache } = useSail(); const { network } = useSolana(); const query = useQuery( makeBatchedParsedAccountQuery(keys, network, fetchKeys, parser, options) ); useAccountsSubscribe(keys); // refresh from the cache whenever the cache is updated const { refetch } = query; useEffect(() => { if (!keys) { return; } return onBatchCache((e) => { if (keys.find((key) => key && e.hasKey(key))) { void refetch(); } }); }, [keys, fetchKeys, onBatchCache, refetch]); return query; };