RVid
RVid

Reputation: 1297

Apollo Client lazy refetch

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
    };
}

Upvotes: 6

Views: 15041

Answers (3)

workteam123
workteam123

Reputation: 21

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

Upvotes: 0

RVid
RVid

Reputation: 1297

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" });

Upvotes: 0

chonnychu
chonnychu

Reputation: 1116

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
    };
}

Upvotes: 5

Related Questions