Skip to content

Instantly share code, notes, and snippets.

@borispoehland
Created September 10, 2023 05:23
Show Gist options
  • Save borispoehland/0903e7374c04591ff90b826d67905b1b to your computer and use it in GitHub Desktop.
Save borispoehland/0903e7374c04591ff90b826d67905b1b to your computer and use it in GitHub Desktop.
Wrap react-query with a per-hook way to invalidate and set queries
import type { SetStateAction } from 'react'
import { useCallback } from 'react'
import type { QueryFunction, UseQueryOptions } from 'react-query'
import { useQuery, useQueryClient } from 'react-query'
type IQueryMutator<T> = (value: SetStateAction<T>) => void
type IOptions<T> =
| Omit<UseQueryOptions<T, unknown, T, string[]>, 'queryKey' | 'queryFn'>
| undefined
export function useAppQuery<T>(
queryKey: string[],
func: QueryFunction<T, typeof queryKey>,
options: IOptions<T> = {}
) {
const queryClient = useQueryClient()
const { data, ...rest } = useQuery(queryKey, func, options)
const refetchData = useCallback(() => {
return queryClient.invalidateQueries({ queryKey })
}, [queryClient, queryKey])
const setDataOnly: IQueryMutator<T> = useCallback(
async (value) => {
queryClient.setQueryData<typeof data>(queryKey, (prev) => {
const prevData = prev ?? []
const newData =
// eslint-disable-next-line @typescript-eslint/ban-types
typeof value === 'function' ? (value as Function)(prevData) : value
return newData
})
},
[queryClient, queryKey]
)
const setData: typeof setDataOnly = useCallback(
(...args) => {
setDataOnly(...args)
return refetchData()
},
[refetchData, setDataOnly]
)
return {
...rest,
data: data ?? [],
setDataOnly,
refetchData,
setData,
}
}
// example usage
function useUsername(userId: number) {
return useAppQuery(
['username', userId],
({ queryKey }) => fetch(`https://myapi.com/byId/${queryKey[1]}`)
)
}
const {
data,
setData,
setDataOnly,
refetchData,
isLoading
} = useUsername(123)
refetchData() // triggers a re-run of the fetch
setData('newusername') // optimistically sets the username, then re-fetches in the background
setDataOnly('newusername') // sets the username only (like setState)
console.log(data, isLoading)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment