- Make
queryKey
strongly related withqueryFn
- Manage
queryKey
in a type-safe way - Generate a custom ReactQuery hook quickly
- Make
queryClient
's operations clearly associated with custom ReactQuery hooks - Set defaultOptions for custom ReactQuery hooks easier and clearer
English | ็ฎไฝไธญๆ
- Installation
- Examples
- Usage
- Issues
- LICENSE
This module is distributed via npm which is bundled with node and
should be installed as one of your project's dependencies
:
$ npm i react-query-kit
# or
$ yarn add react-query-kit
import { QueryClient, dehydrate } from '@tanstack/react-query'
import { createQuery } from 'react-query-kit'
type Response = { title: string; content: string }
type Variables = { id: number }
const usePost = createQuery<Response, Variables, Error>({
primaryKey: '/posts',
queryFn: ({ queryKey: [primaryKey, variables] }) => {
// primaryKey equals to '/posts'
return fetch(`${primaryKey}/${variables.id}`).then(res => res.json())
},
// if u only wanna fetch once
enabled: (data) => !data,
suspense: true
})
// or using the alternative syntax to create
// const usePost = createQuery<Response, Variables, Error>(
// '/posts',
// ({ queryKey: [primaryKey, variables] }) => {
// // primaryKey equals to '/posts'
// return fetch(`${primaryKey}/${variables.id}`).then(res => res.json())
// },
// {
// // if u only wanna fetch once
// enabled: (data) => !data,
// suspense: true
// }
// )
const variables = { id: 1 }
// example
export default function Page() {
// queryKey equals to ['/posts', { id: 1 }]
const { data } = usePost({ variables, suspense: true })
return (
<div>
<div>{data?.title}</div>
<div>{data?.content}</div>
</div>
)
}
console.log(usePost.getKey()) // ['/posts']
console.log(usePost.getKey(variables)) // ['/posts', { id: 1 }]
// nextjs example
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(usePost.getKey(variables), usePost.queryFn)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
// usage outside of react component
const data = await queryClient.fetchQuery(
usePost.getKey(variables),
usePost.queryFn
)
// useQueries example
const queries = useQueries({
queries: [
{ queryKey: usePost.getKey(variables), queryFn: usePost.queryFn },
{ queryKey: useProjects.getKey(), queryFn: useProjects.queryFn },
],
})
// setQueryData
queryClient.setQueryData(usePost.getKey(variables), {...})
Options
primaryKey: string
- Required
primaryKey
will be the first element of the array ofqueryKey
enabled: boolean | ((data: TData) => boolean)
- Optional
- Set this to
false
to disable this query from automatically running. - If set to a function, the function will be executed with the latest data to compute the boolean
Expose Methods
getPrimaryKey: () => primaryKey
getKey: (variables: TVariables) => [primaryKey, variables]
queryFn: QueryFunction<TFnData, [primaryKey, TVariables]>
Returns
queryKey: unknown[]
- The query key of this custom query.
setData: (updater: Updater<TData>, options?: SetDataOptions) => TData | undefined
- it's args similar with
queryClient.setQueryData
but withoutqueryKey
- it's args similar with
import { QueryClient, dehydrate } from '@tanstack/react-query'
import { createInfiniteQuery } from 'react-query-kit'
type Data = { projects: { id: string; name: string }[]; nextCursor: number }
type Variables = { active: boolean }
const useProjects = createInfiniteQuery<Data, Variables, Error>({
primaryKey: 'projects',
queryFn: ({ queryKey: [_primaryKey, variables], pageParam = 1 }) => {
return fetch(`/projects?cursor=${pageParam}?active=${variables.active}`).then(res => res.json())
},
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
})
const variables = { active: true }
// example
export default function Page() {
// queryKey equals to ['projects', { active: true }]
const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =
useProjects({ variables, suspense: true })
return (
<div>
{data.pages.map((group, i) => (
<React.Fragment key={i}>
{group.projects.map(project => (
<p key={project.id}>{project.name}</p>
))}
</React.Fragment>
))}
<div>
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? 'Loading more...'
: hasNextPage
? 'Load More'
: 'Nothing more to load'}
</button>
</div>
<div>{isFetching && !isFetchingNextPage ? 'Fetching...' : null}</div>
</div>
)
}
// nextjs example
export async function getStaticProps() {
const queryClient = new QueryClient()
await queryClient.prefetchInfiniteQuery(
useProjects.getKey(variables),
useProjects.queryFn
)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
// usage outside of react component
const data = await queryClient.fetchInfiniteQuery(
useProjects.getKey(variables),
useProjects.queryFn
)
Options
primaryKey: string
- Required
primaryKey
will be the first element of the arrary ofqueryKey
enabled: boolean | ((data: TData) => boolean)
- Optional
- Set this to
false
to disable this query from automatically running. - If set to a function, the function will be executed with the latest data to compute the boolean
Expose Methods
getPrimaryKey: () => primaryKey
getKey: (variables: TVariables) => [primaryKey, variables]
queryFn: QueryFunction<TFnData, [primaryKey, TVariables]>
Returns
queryKey: unknown[]
- The query key of this custom query.
setData: (updater: Updater<InfiniteData<TFnData>>, options?: SetDataOptions) => TData | undefined
- it's args similar with
queryClient.setQueryData
but withoutqueryKey
- it's args similar with
import { createMutation } from 'react-query-kit'
const useAddTodo = createMutation(
async (data: { title: string; content: string }) =>
fetch('/post', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then(res => res.json()),
{
onSuccess(data, variables, context) {
// do somethings
},
}
)
function App() {
const mutation = useAddTodo({
onSettled: (data, error, variables, context) => {
// Error or success... doesn't matter!
}
})
return (
<div>
{mutation.isLoading ? (
'Adding todo...'
) : (
<>
{mutation.isError ? (
<div>An error occurred: {mutation.error.message}</div>
) : null}
{mutation.isSuccess ? <div>Todo added!</div> : null}
<button
onClick={() => {
mutation.mutate({ title: 'Do Laundry', content: "content..." })
}}
>
Create Todo
</button>
</>
)}
</div>
)
}
// usage outside of react component
useAddTodo.mutationFn({ title: 'Do Laundry', content: "content..." })
Returns
getKey: () => MutationKey
mutationFn: MutationFunction<TData, TVariables>
Looking to contribute? Look for the Good First Issue label.
Please file an issue for bugs, missing documentation, or unexpected behavior.
Please file an issue to suggest new features. Vote on feature requests by adding a ๐. This helps maintainers prioritize what to work on.
MIT