Apollo Client lazy refetch

Viewed 8620

In Apollo Client v3 React implementation, I am using hooks to use subscription. When I receive data from subscription I would like to refetch query but only if query has been previously executed and is in cache. Is there a way to achieve this?

I have started by having a lazy query and then checking the cache manually when subscription data received and then trying to execute lazy query and refetch. It works but it just feels clunky...

export const useMyStuffLazyRefetch = () => {
    const [refetchNeeded, setRefetchNeeded] = useState<boolean>(false);
    const client = useApolloClient();
    const [getMyStuff, { data, refetch }] = useLazyQuery<IStuffData>(GET_MY_STUFF);

    useEffect(() => {
        if (refetchNeeded) {
            setRefetchNeeded(false);
            refetch();
        }
    }, [refetchNeeded]);

    const refetchIfNeeded = async () => {
        const stuffData = client.cache.readQuery<IStuffData>({ query: GET_MY_STUFF });
        if (!stuffData?.myStuff?.length) return;
        getMyStuff();
        setRefetchNeeded(true);
    }

    return {
        refetchIfNeeded: refetchIfNeeded
    };
}
3 Answers

useLazyQuery has a prop called called, this is a boolean indicating if the query function has been called,

so maybe you can try this:

export const useMyStuffLazyRefetch = () => {
    const [refetchNeeded, setRefetchNeeded] = useState<boolean>(false);
    const client = useApolloClient();
    const [getMyStuff, { data, refetch, called }] = useLazyQuery<IStuffData>(GET_MY_STUFF);

    useEffect(() => {
        if (refetchNeeded) {
            setRefetchNeeded(false);

            if (called) {
              refetch();
            }
            else {
              getMyStuff()
            }
        }
    }, [refetchNeeded, called]);

    const refetchIfNeeded = async () => {
        const stuffData = client.cache.readQuery<IStuffData>({ query: GET_MY_STUFF });
        if (!stuffData?.myStuff?.length) return;
        getMyStuff();
        setRefetchNeeded(true);
    }

    return {
        refetchIfNeeded: refetchIfNeeded
    };
}

In case this can help to somebody. I have created a separate hook so the usage is less of an eyesore.

This is the hook to refetch if data is in cache. If the data is not in the cache, Apollo Client errors instead of returning something like undefined or null

import { useState, useEffect } from "react";
import { OperationVariables, DocumentNode, LazyQueryHookOptions, useApolloClient, useLazyQuery } from "@apollo/client";

export default function useLazyRefetch <TData = any, TVariables = OperationVariables>(query: DocumentNode, options?: LazyQueryHookOptions<TData, TVariables>) {
    const [refetchNeeded, setRefetchNeeded] = useState<boolean>(false);
    const [loadData, { refetch }] = useLazyQuery(query, options);
    const client = useApolloClient();

    useEffect(() => {
        if (refetchNeeded) {
            setRefetchNeeded(false);
            refetch();
        }
    }, [refetchNeeded]);

    const refetchIfNeeded = (variables: TVariables) => {
        try {
            const cachecData = client.cache.readQuery<
                TData,
                TVariables
            >({
                query: query,
                variables: variables
            });
            if (!cachecData) return;
            loadData({ variables: variables });
            setRefetchNeeded(true);
        }
        catch {}
    };

    return {
        refetchIfNeeded: refetchIfNeeded
    };
}

And the hook usage example:

const { refetchIfNeeded } = useLazyRefetch<
        IStuffData,
        { dataId?: string }
    >(GET_MY_STUFF);

//... And then you can just call it when you need to

refetchIfNeeded({ dataId: "foo" });

typescript is complaining in your

   useEffect(() => {
   if (refetchNeeded) {
       setRefetchNeeded(false);
       refetch();
   }
}, [refetchNeeded]);

refetch() says - Cannot invoke an object which is possibly 'undefined'.ts(2722)

const refetch: ((variables?: Partial<TVariables> | undefined) => Promise<ApolloQueryResult<TData>>) | undefined

    and in [refetchNeeded] dependency - 

React Hook useEffect has a missing dependency: 'refetch'. Either include it or remove the dependency array.eslintreact-hooks/exhaustive-deps const refetchNeeded: boolean

Related