Home > Blockchain >  useQuery used in custom hook returns a response with type useQueryResults<unknown, unknown>
useQuery used in custom hook returns a response with type useQueryResults<unknown, unknown>

Time:10-15

I have the following custom hook called useFocusRefetchQuery

type Props = Parameters<typeof useQuery>;

const useFocusRefetchQuery = (...params: Props) => {
    const useQueryResults = useQuery(params);

    useFocusEffect(
        React.useCallback(() => {
            useQueryResults.refetch();
        }, [useQueryResults])
    );

    return useQueryResults;
};

Which I then call in useGetArticle like this:

export const useGetArticle = (articleId: string) => {
    return useFocusRefetchQuery(['getArticle', { articleId }], () => getArticle(articleId), { staleTime: 0 });
};

And useGetArticle is called like this

const { data: article, isLoading } = useGetArticle('home');

My problem is that in this case, article has a type of unknown and I can't figure out why is this happening. My guess is that I have to somehow type what is being returned by useFocusRefetchQuery. Before I implemented the custom hook, Typescript automatically inferred the return type of useQuery

CodePudding user response:

I don't recall typescript infering anything from the GQL request string, in my experience you need to provide useQuery the expected output and input (variables) types as mentioned here in details. The useQuery signature is as follow:

useQuery<TData = any, TVariables = OperationVariables>

In your case you would have to add both Props and your article type, say Article:

import { gql, QueryHookOptions, useQuery } from '@apollo/client';

const GET_ARTICLE_QUERY = gql`
  query getArticle {
    article: {
      id,
      articleId,
      articleName,
    }
  }
`

interface ArticleContent {
  __typename: "Article";
  id: string;
  articleId: string;
  articleName: string;
}

interface Article {
  article: ArticleContent | null;
}

interface ArticleInput {
  articleId: string;
}

const useArticleQuery = (options: QueryHookOptions = {}) => {
    return useQuery<Article, ArticleInput>(GET_ARTICLE_QUERY);
}

I think its also a good idea to keep the options (QueryHookOptions) in here so you can use your hook in different contexts.

You can use a tool like Graphql code generator to automatically generate the static types Article and ArticleInput for you (based on your schema and your JS gql requests).

If you want to have a generic hook for several useQuery that all look the same (for example they all use useFocusEffect), you will have to do something like this:

function useFocusRefetchQuery<TData, TVariables>(){
    const useQueryResults = useQuery<TData, TVariables>();
    // ...
}

This way you can use useFocusRefetchQuery instead of useQuery, but I really think you will have to pass both the input/output types, which means in the previous example you would have:

const useArticleQuery = (options: QueryHookOptions = {}) => {
    return useFocusRefetchQuery<Article, ArticleInput>(GET_ARTICLE_QUERY);
}
  • Related