UNPKG

@macalinao/grill

Version:

Modern Solana development kit for React applications with automatic account batching, caching, and transaction notifications

105 lines (100 loc) 2.93 kB
import { useQueries } from "@tanstack/react-query"; import type { Account, Address, Decoder, FetchAccountConfig, Simplify, } from "gill"; import { useGrillContext } from "../contexts/grill-context.js"; import { createAccountQueryKey, fetchAndDecodeAccount, } from "../utils/account-helpers.js"; import type { GillUseRpcHook } from "./types.js"; type RpcConfig = Simplify<Omit<FetchAccountConfig, "abortSignal">>; export type UseAccountsInput< TConfig extends RpcConfig = RpcConfig, TDecodedData extends object = Uint8Array, > = GillUseRpcHook<TConfig> & { /** * Addresses of the accounts to get the info of. */ addresses: (Address | null | undefined)[]; /** * Account decoder that can decode the account's `data` byte array value. * * Note: if not provided, the account will be returned as a `Uint8Array`. */ decoder?: Decoder<TDecodedData>; }; export type UseAccountsResult<TDecodedData extends object> = | { isLoading: true; data: (Account<TDecodedData> | null | undefined)[]; addresses: (Address | null | undefined)[]; } | { isLoading: false; data: (Account<TDecodedData> | null)[]; addresses: (Address | null | undefined)[]; }; /** * Get account info for multiple addresses with automatic batching via DataLoader. * Concurrent queries are automatically batched into a single RPC call. * * @example * ```tsx * const tokenAccounts = useAccounts({ * addresses: [tokenAccount1, tokenAccount2, tokenAccount3], * decoder: tokenAccountDecoder, * }); * * // Access individual results * tokenAccounts.forEach((result, index) => { * if (result.data) { * console.log(`Account ${index}:`, result.data); * } * }); * ``` */ export function useAccounts< TConfig extends RpcConfig = RpcConfig, TDecodedData extends object = Uint8Array, >({ options, addresses, decoder, }: UseAccountsInput<TConfig, TDecodedData>): UseAccountsResult<TDecodedData> { const { accountLoader } = useGrillContext(); return useQueries({ queries: addresses.map((address) => ({ networkMode: "offlineFirst" as const, ...options, // eslint-disable-next-line @tanstack/query/exhaustive-deps queryKey: address ? createAccountQueryKey(address) : [null], queryFn: (): Promise<Account<TDecodedData> | null> => fetchAndDecodeAccount(address, accountLoader, decoder), enabled: !!address, })), combine: (results) => { const isLoading = results.some( (result) => result.isLoading || result.data === undefined, ); if (isLoading) { return { isLoading: true, data: results.map((result) => result.data), addresses, }; } return { isLoading: false, data: results .map((result) => result.data) .filter((r) => r !== undefined), addresses, }; }, }); }