- Make
queryKeystrongly related withqueryFn - Manage
queryKeyin 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-kitimport { QueryClient, dehydrate } from '@tanstack/react-query'
import { createQuery, inferData } 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<inferData<typeof usePost>>(usePost.getKey(variables), {...})Options
primaryKey: string- Required
primaryKeywill be the first element of the array ofqueryKey
enabled: boolean | ((data: TData, variables: TVariables) => boolean)- Optional
- Set this to
falseto 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: () => primaryKeygetKey: (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.setQueryDatabut 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
primaryKeywill be the first element of the arrary ofqueryKey
enabled: boolean | ((data: TData, variables: TVariables) => boolean)- Optional
- Set this to
falseto 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: () => primaryKeygetKey: (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.setQueryDatabut withoutqueryKey
- it's args similar with
import { createMutation } from 'react-query-kit'
const useAddTodo = createMutation(
async (variables: { title: string; content: string }) =>
fetch('/post', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(variables),
}).then(res => res.json()),
{
onSuccess(data, variables, context) {
// do somethings
},
}
)
// or using the alternative syntax to create
// const useAddTodo = createMutation<TData, { title: string; content: string }>(
// async (variables) =>
// fetch('/post', {
// method: 'POST',
// headers: {
// Accept: 'application/json',
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify(variables),
// }).then(res => res.json()),
// )
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: () => MutationKeymutationFn: MutationFunction<TData, TVariables>
You can extract the TypeScript type of any custom hook with inferVariables or inferData
import { inferVariables, inferData } from 'react-query-kit'
type Variables = inferVariables<typeof usePost>
type Data = inferData<typeof usePost>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
