Skip to main content
Module

x/jotai/docs/integrations/query.mdx

👻 Primitive and flexible state management for React
Go to Latest
File
---title: Querydescription: This doc describes `jotai/query` bundle.nav: 4.03---
This is the Jotai integration with [TanStack Query v4](https://tanstack.com/query/v4), a set of hooks and tools for managing async state (typically external data).
From the [Overview docs](https://tanstack.com/query/v4/docs/overview):
> React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your React applications a breeze.Jotai with TanStack Query provides a wonderful interface with all of the TanStack Query features, providing you the ability to use those features in combination with your existing Jotai state.
## Install
In addition to `jotai`, you have to install `@tanstack/query-core` to make use of this bundle and its functions.
```bashyarn add @tanstack/query-core```
## atomWithQuery
`atomWithQuery` creates a new atom that implements a standard [`Query`](https://tanstack.com/query/v4/docs/guides/queries) from TanStack Query. This function combines both Jotai `atom` features and `useQuery` features in a single atom. This atom supports all features you'd expect to find when using the standard `@tanstack/react-query` package.
> A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server.```jsximport { useAtom } from 'jotai'import { atomWithQuery } from 'jotai/query'
const idAtom = atom(1)const userAtom = atomWithQuery((get) => ({ queryKey: ['users', get(idAtom)], queryFn: async ({ queryKey: [, id] }) => { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`) return res.json() },}))
const UserData = () => { const [data] = useAtom(userAtom) return <div>{JSON.stringify(data)}</div>}```
## atomWithInfiniteQuery
`atomWithInfiniteQuery` is very similar to `atomWithQuery`, however it creates an `InfiniteQuery`, which is used for data that is meant to be paginated. You can [read more about Infinite Queries here](https://tanstack.com/query/v4/docs/guides/infinite-queries). This atom supports all features you'd expect to find when using the standard `@tanstack/react-query` package.
> Rendering lists that can additively "load more" data onto an existing set of data or "infinite scroll" is also a very common UI pattern. React Query supports a useful version of useQuery called useInfiniteQuery for querying these types of lists.A notable difference between a standard query atom is the additional option `getNextPageParam` and `getPreviousPageParam`, which is what you'll use to instruct the query on how to fetch any additional pages.
```jsximport { useAtom } from 'jotai'import { atomWithInfiniteQuery } from 'jotai/query'
const idAtom = atom(1)const userAtom = atomWithInfiniteQuery((get) => ({ queryKey: ['users', get(idAtom)], queryFn: async ({ queryKey: [, id] }) => { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`) return res.json() }, // infinite queries can support paginated fetching getNextPageParam: (lastPage, pages) => lastPage.nextCursor,}))
const UserData = () => { const [data] = useAtom(userAtom) return data.pages.map((userData, index) => ( <div key={index}>{JSON.stringify(userData)}</div> ))}```
### Fetching pages and refetching
Using the same atom as in the above example, we can use the `update` method returned from `useAtom` to call different `@tanstack/react-query` methods:
```jsconst UserData = () => { const [data, update] = useAtom(userAtom) const handleFetchNextPage = () => update({ type: 'fetchNextPage' }) const handleFetchPreviousPage = () => update({ type: 'fetchPreviousPage' }) const handleRefetchAllPages = () => update({ type: 'refetch' })}```
## Referencing the same instance of Query Client in your project
Perhaps you have some custom hooks in your project that utilises the `useQueryClient()` hook to obtain the `QueryClient` object and call its methods.
To ensure that you reference the same `QueryClient` object, be sure to wrap the root of your project in a `<Provider>` and initialise `queryClientAtom` with the same `queryClient` value you provided to `QueryClientProvider`.
Without this step, `useQueryAtom` will reference a seperate `QueryClient` from any hooks that utilise the `useQueryClient()` hook to get the queryClient.
### Example
In the example below, we have a mutation hook, `useTodoMutation` and a query `todosAtom`.
We included an initialisation step in our root `<App>` node.
Although they reference methods same query key (`'todos'`), the `onSuccess` invalidation in `useTodoMutation` will not trigger **if the `Provider` initialisation step was not done.**
This will result in `todosAtom` showing stale data as it was not prompted to refetch.
```jsximport { useMutation, useQueryClient, QueryClient, QueryClientProvider,} from '@tanstack/react-query'import { atomWithQuery } from 'jotai/query'
const queryClient = new QueryClient()export const App = () => { return ( <QueryClientProvider client={queryClient}> {/* This Provider initalization step is needed so that we reference the same queryClient in both atomWithQuery and other parts of the app. Without this, our useQueryClient() hook will return a different QueryClient object */} <Provider initialValues={[[queryClientAtom, queryClient]]}> <App /> </Provider> </QueryClientProvider> )}
export const todosAtom = atomWithQuery((get) => { return { queryKey: ['todos'], queryFn: () => fetch('/todos'), }})
export const useTodoMutation = () => { const queryClient = useQueryClient() return useMutation( async (body: todo) => { await fetch('/todo', { Method: 'POST', Body: body }) }, { onSuccess: () => { void queryClient.invalidateQueries(['todos']) }, onError, } )}```
## SSR support
Both atoms can be used within the context of a server side rendered app, such as a next.js app or Gatsby app. You can [use both options](https://tanstack.com/query/v4/docs/guides/ssr) that React Query supports for use within SSR apps, [hydration](https://tanstack.com/query/v4/docs/guides/ssr#using-hydration) or [`initialData`](https://tanstack.com/query/v4/docs/guides/ssr#using-initialdata).
## Error handling
Fetch error will be thrown and can be caught with ErrorBoundary.Refetching may recover from a temporary error.
See [a working example](https://codesandbox.io/s/04mepx) to learn more.
## Examples
### Basic demo
<CodeSandbox id="ij2sd" />### Hackernews
<CodeSandbox id="u4sli" />