66 * Copyright Oxide Computer Company
77 */
88import {
9+ hashKey ,
10+ queryOptions ,
911 useMutation ,
1012 useQueries ,
1113 useQuery ,
1214 type DefaultError ,
1315 type FetchQueryOptions ,
1416 type InvalidateQueryFilters ,
1517 type QueryClient ,
18+ type QueryKey ,
1619 type UndefinedInitialDataOptions ,
1720 type UseMutationOptions ,
1821 type UseQueryOptions ,
22+ type UseQueryResult ,
1923} from '@tanstack/react-query'
2024import { type SetNonNullable } from 'type-fest'
2125
@@ -100,14 +104,14 @@ type FetchQueryOtherOptions<T, E = DefaultError> = Omit<
100104 'queryKey' | 'queryFn'
101105>
102106
103- export const getUseApiQuery =
107+ export const getApiQueryOptions =
104108 < A extends ApiClient > ( api : A ) =>
105109 < M extends string & keyof A > (
106110 method : M ,
107111 params : Params < A [ M ] > ,
108112 options : UseQueryOtherOptions < Result < A [ M ] > , ApiError > = { }
109- ) => {
110- return useQuery ( {
113+ ) =>
114+ queryOptions ( {
111115 queryKey : [ method , params ] ,
112116 // no catch, let unexpected errors bubble up
113117 queryFn : ( { signal } ) => api [ method ] ( params , { signal } ) . then ( handleResult ( method ) ) ,
@@ -118,7 +122,15 @@ export const getUseApiQuery =
118122 throwOnError : ( err ) => err . statusCode === 404 ,
119123 ...options ,
120124 } )
121- }
125+
126+ export const getUseApiQuery =
127+ < A extends ApiClient > ( api : A ) =>
128+ < M extends string & keyof A > (
129+ method : M ,
130+ params : Params < A [ M ] > ,
131+ options : UseQueryOtherOptions < Result < A [ M ] > , ApiError > = { }
132+ ) =>
133+ useQuery ( getApiQueryOptions ( api ) ( method , params , options ) )
122134
123135export const getUsePrefetchedApiQuery =
124136 < A extends ApiClient > ( api : A ) =>
@@ -127,33 +139,32 @@ export const getUsePrefetchedApiQuery =
127139 params : Params < A [ M ] > ,
128140 options : UseQueryOtherOptions < Result < A [ M ] > , ApiError > = { }
129141 ) => {
130- const queryKey = [ method , params ]
131- const result = useQuery ( {
132- queryKey,
133- // no catch, let unexpected errors bubble up
134- queryFn : ( { signal } ) => api [ method ] ( params , { signal } ) . then ( handleResult ( method ) ) ,
142+ const qOptions = getApiQueryOptions ( api ) ( method , params , options )
143+ return ensure ( useQuery ( qOptions ) , qOptions . queryKey )
144+ }
135145
136- // we can say Not Found. If you need to allow a 404 and want it to show
137- // up as `error` state instead, pass `useErrorBoundary: false` as an
138- // option from the calling component and it will override this
139- throwOnError : ( err ) => err . statusCode === 404 ,
140- ...options ,
141- } )
142- invariant (
143- result . data ,
144- `Expected query to be prefetched.
145- Key: ${ JSON . stringify ( queryKey ) }
146+ const prefetchError = ( key ?: QueryKey ) =>
147+ `Expected query to be prefetched.
148+ Key: ${ key ? hashKey ( key ) : '<unknown>' }
146149Ensure the following:
147150• loader is called in routes.tsx and is running
148151• query matches in both the loader and the component
149152• request isn't erroring-out server-side (check the Networking tab)
150- • mock API endpoint is implemented in handlers.ts
151- `
152- )
153- // TS infers non-nullable on a freestanding variable, but doesn't like to do
154- // it on a property. So we give it a hint
155- return result as SetNonNullable < typeof result , 'data' >
156- }
153+ • mock API endpoint is implemented in handlers.ts`
154+
155+ export function ensure < TData , TError > (
156+ result : UseQueryResult < TData , TError > ,
157+ /**
158+ * Optional because if we call this manually from a component like
159+ * `ensure(useQuery(...))`, * we don't necessarily have access to the key.
160+ */
161+ key ?: QueryKey
162+ ) {
163+ invariant ( result . data , prefetchError ( key ) )
164+ // TS infers non-nullable on a freestanding variable, but doesn't like to do
165+ // it on a property. So we give it a hint
166+ return result as SetNonNullable < typeof result , 'data' >
167+ }
157168
158169const ERRORS_ALLOWED = 'errors-allowed'
159170
0 commit comments