reactjsreact-queryweb3-react

How auto refetch react-query's useQuery for dynamic query function


I am trying to use useQuery for smart contract interaction.

The problem is, the contract return from useContract is null initially, as library and chainId are null initially.

So, the first fetch from useQuery is using a null contract to call the fetchPaginated function. Even library and chainId got initialised such that contract is not null, the useQuery does not get refetched.

The contract is a complex object that can't be set as query keys.

How to trigger the refetch for this kind of dynamic query function in useQuery?

Below are the codes:

// hook for handling contract
export function useContract<T extends Contract = Contract>(
  address: string,
  ABI: any,
  withSignerIfPossible = true,
): T | null {
  const { library, account, chainId } = useWeb3React();

  return useMemo(() => {
    if (!address || !ABI || !library || !chainId) return null;
    try {
      return getContract(
        address,
        ABI,
        library,
        withSignerIfPossible && account ? account : undefined,
      );
    } catch (error) {
      console.error('Failed to get contract', error);
      return null;
    }
  }, [address, ABI, library, chainId, withSignerIfPossible, account]) as T;
}

// The useQuery that want to get refetched
export const useFetchERC20Contracts = (
  page: number,
  pageSize: number,
) => {
  const contract = useContract();

  const result = useQuery<Response>(
    ['contract', page, pageSize],
    async () => {
      if (!contract) {
        return [];
      }
      return contract.fetchPaginated(page, pageSize);
    },
    { cacheTime: 0 },
  );

  return result;
};

Solution

  • everything that the queryFn depends on and that should drive the query to re-fetch should go into the query key. If you can't put contract there for some reason, you have to add its dependencies, in this case:

    address, ABI, library, chainId, withSignerIfPossible, account
    

    generally, I wouldn't separate this into two hooks. I would just have the queryFn call getContract directly, because that makes the dependencies obvious.