Created
October 5, 2025 06:30
-
-
Save lolmaus/d2a5ab2c9d9eb16a6187162047479fc9 to your computer and use it in GitHub Desktop.
useDerivedQuery — using TanStack Query as a state machine for derived state from another query
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const USE_JUNCTIONS_QUERY_KEY = 'data/junction/fetchJunctions' as const; | |
export const USE_JUNCTIONS_STALE_TIME = ONE_DAY; | |
export function useJunctions(): UseQueryResult<TJunctionFeature[], TAppError> { | |
return useQueryUnwrapResult<TPaginatedPayload<TJunction>, TJunctionFeature[]>( | |
{ | |
queryKey: [USE_JUNCTIONS_QUERY_KEY], | |
staleTime: USE_JUNCTIONS_STALE_TIME, | |
gcTime: USE_JUNCTIONS_STALE_TIME, | |
queryFnResult: ({ signal }) => fetchJunctions({ signal }), | |
select(data: TPaginatedPayload<TJunction>): TJunctionFeature[] { | |
return data.data.map(junctionToJunctionFeature); | |
}, | |
} | |
); | |
} | |
export function useJunctionsById(): UseQueryResult< | |
Record<number, TJunctionFeature> | undefined, | |
TAppError | |
> { | |
return useDerivedQuery({ | |
useQueryResult: useJunctions(), | |
select: (junctions) => | |
junctions && keyBy(junctions, (j) => j.properties.id), | |
queryKey: [USE_JUNCTIONS_QUERY_KEY, 'by id'], | |
staleTime: USE_JUNCTIONS_STALE_TIME, | |
}); | |
} | |
export function useJunction( | |
junctionId: number | |
): UseQueryResult<TJunctionFeature | undefined, TAppError> { | |
return useDerivedQuery({ | |
useQueryResult: useJunctionsById(), | |
select: (junctionsById) => junctionsById?.[junctionId], | |
queryKey: [USE_JUNCTIONS_QUERY_KEY, 'by id', junctionId], | |
staleTime: USE_JUNCTIONS_STALE_TIME, | |
}); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { TAppError } from '@/utils/errors'; | |
import { | |
useQuery, | |
type RefetchOptions, | |
type UseQueryResult, | |
} from '@tanstack/react-query'; | |
export function useDerivedQuery<TParentData, TDerivedData>({ | |
useQueryResult: useQueryResultParent, | |
queryKey, | |
select, | |
staleTime, | |
}: { | |
useQueryResult: UseQueryResult<TParentData, TAppError>; | |
queryKey: unknown[]; | |
select: (data: TParentData | undefined) => TDerivedData | undefined; | |
staleTime: number; | |
}): UseQueryResult<TDerivedData | undefined, TAppError> { | |
const useQueryResultDerived = useQuery<null, never, TDerivedData | undefined>( | |
{ | |
queryKey, | |
staleTime, | |
gcTime: staleTime, | |
queryFn: () => null, | |
select: () => { | |
return select(useQueryResultParent.data); | |
}, | |
enabled: useQueryResultParent.isSuccess, | |
} | |
); | |
const { data: derivedData } = useQueryResultDerived; | |
const refetch: UseQueryResult< | |
TDerivedData | undefined, | |
TAppError | |
>['refetch'] = async (options?: RefetchOptions) => { | |
await useQueryResultParent.refetch(options); | |
return useQueryResultDerived; | |
}; | |
const promise = useQueryResultParent.promise.then((data) => select(data)); | |
if (useQueryResultParent.isPending) { | |
return { | |
...useQueryResultParent, | |
data: undefined, | |
refetch, | |
promise, | |
}; | |
} | |
if (useQueryResultParent.isError) { | |
return { | |
...useQueryResultParent, | |
data: undefined, | |
refetch, | |
promise, | |
}; | |
} | |
return { | |
...useQueryResultParent, | |
data: derivedData, | |
refetch, | |
promise: Promise.resolve(derivedData), | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm not saying it's a great idea, but it's an idea...