Reputation: 1297
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
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
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
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