Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helpers for extracting variables / options #48

Open
JanStevens opened this issue Jan 12, 2023 · 1 comment
Open

Add helpers for extracting variables / options #48

JanStevens opened this issue Jan 12, 2023 · 1 comment

Comments

@JanStevens
Copy link

JanStevens commented Jan 12, 2023

Hi,

Typically we use react-query in the following way, by writing a small reusable hook, ex:

// Example of request vars
type EventRequestVariables {
  count: number;
  filter: Record<string, string>,
  skip: number;
}

// in queries.ts
export const EventsQueries = createQueryKeys('events', {
  // bunch of other events related queries
  events: (variables: EventRequestVariables) => ({
    queryKey: [variables],
    queryFn: () => api.events(variables),
  }),
})


// in useEvents.ts
export const useEvents = (
  variables?: QueryVariables<typeof EventsQueries.events>,
  options?: CommonQueryOptions<typeof EventsQueries.events>,
) => {
  const { locale } = useLocale();

  const { data, status, isLoading, isError, refetch } = useQuery({
    ...EventsQueries.events({ locale, ...variables }),
    staleTime: StaleTime.FOREVER,
    ...options,
  });

  return {
    data: data?.allPageEvents || [],
    status,
    isLoading,
    isError,
    refetch,
  };
};

This allows us to easily expose what we need from useQuery while allowing to reuse useEvents across our application. I've used 2 helper types to extract the variables and query options from the QueryKey factory instance.

They could use some love but I think it might be a great addition to this library, also potentially solving issue #40 without making the typings less strict.

// We need to use any here for generics
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type GenericFunction<T = any> = (...args: any[]) => T;

// Extracts the query function
type ExtractQueryFn<T> = T extends GenericFunction<{
  queryFn: infer QueryFn;
}>
  ? QueryFn extends GenericFunction
    ? QueryFn
    : never
  : never;

// Extracts the queryData aka the returned Data
type ExtractQueryData<T> = T extends GenericFunction<{
  queryFn: infer QueryFn;
}>
  ? QueryFn extends GenericFunction
    ? Awaited<ReturnType<QueryFn>>
    : never
  : never;

// Extract the query key
type ExtractQueryKey<T> = T extends GenericFunction<{
  queryKey: infer TQueryKey;
}>
  ? TQueryKey extends QueryKey
    ? TQueryKey
    : never
  : never;

// You can use this to allow passing in custom variables
export type QueryVariables<
  T extends GenericFunction,
> = Parameters<T>[0];

// You can use this to pass correct Query options, allowing to pass a custom error
export type CommonQueryOptions<
  T extends GenericFunction,
  TError = Error,
  TQueryData = ExtractQueryData<T>,
  TQueryKey extends QueryKey = ExtractQueryKey<T>,
> = UseQueryOptions<TQueryData, TError, TQueryData, TQueryKey>;

Right now it always assumes the Keys are a function, I couldn't really manage to wrap my head around the different options like passing null or returning a string

WDYT?

@joeyfigaro
Copy link

Is this discussion still open?

@JanStevens did you end up using this in any of your work? I'm currently having issues with query function return values if they return a promise. Looks like TS doesn't like something about GenericFunction<any>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants