Skip to content

Instantly share code, notes, and snippets.

@Svish
Created March 8, 2021 09:47

Revisions

  1. Svish created this gist Mar 8, 2021.
    48 changes: 48 additions & 0 deletions QueryProvider.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    import React, { useMemo } from 'react';
    import {
    QueryClientProvider,
    QueryClient,
    QueryCache,
    MutationCache,
    } from 'react-query';
    import { ReactQueryDevtools } from 'react-query/devtools';

    import { createDefaultQueryFn } from './defaultQueryFn';

    interface Props {
    baseUrl: string;
    children: React.ReactNode;
    }

    export default function QueryProvider({
    baseUrl,
    children,
    }: Props): React.ReactElement {
    const queryClient = useMemo(
    () =>
    new QueryClient({
    queryCache,
    mutationCache,
    defaultOptions: {
    queries: {
    cacheTime: 600000, // 10 min
    staleTime: 60000, // 1 min
    retry: false,
    refetchOnWindowFocus: false,
    queryFn: createDefaultQueryFn(baseUrl),
    },
    },
    }),
    [baseUrl]
    );

    return (
    <QueryClientProvider client={queryClient}>
    {children}
    <ReactQueryDevtools />
    </QueryClientProvider>
    );
    }

    const queryCache = new QueryCache();
    const mutationCache = new MutationCache();
    28 changes: 28 additions & 0 deletions createDefaultQueryFn.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    import { QueryFunction, QueryFunctionContext } from 'react-query';
    import qs, { QueryParams } from './queryString';
    import transformResponse from './transformResponse';

    export type QueryKey = (string | number | undefined | null | QueryParams)[];

    export function createDefaultQueryFn(baseUrl: string): QueryFunction<unknown> {
    return async ({
    queryKey,
    }: QueryFunctionContext<Exclude<QueryKey, string>>) => {
    if (queryKey.some((x) => x == null))
    throw new Error(`Query key cannot include null or undefined`);

    const path = queryKey
    .map((k) => (typeof k === 'object' ? qs(k) : String(k)))
    .join('/');

    const res = await fetch(baseUrl + path, {
    credentials: 'include',
    });

    if (!res.ok) {
    throw new Error(await res.text());
    }

    return transformResponse(await res.text());
    };
    }
    25 changes: 25 additions & 0 deletions useQuery.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    import {
    useQuery as useQueryWrapped,
    UseQueryOptions,
    UseQueryResult,
    } from 'react-query';
    import { QueryKey } from './defaultQueryFn';

    /**
    * Wraps `useQuery`, only allowing query keys compatible with our
    * default query function.
    */
    export default function useQuery<
    TQueryFnData,
    TError = unknown,
    TData = TQueryFnData
    >(
    queryKey: QueryKey,
    options?: UseQueryOptions<TQueryFnData, TError, TData>
    ): UseQueryResult<TData, TError> {
    return useQueryWrapped<TQueryFnData, TError, TData>(queryKey, {
    ...options,
    // Disable if query key includes null or undefined
    enabled: !queryKey.some((x) => x == null) && options?.enabled !== false,
    });
    }