diff --git a/docs/src/manifests/manifest.json b/docs/src/manifests/manifest.json index fbd14fc31a..12c6bc8ce4 100644 --- a/docs/src/manifests/manifest.json +++ b/docs/src/manifests/manifest.json @@ -332,6 +332,16 @@ "path": "/reference/QueryCache", "editUrl": "/reference/QueryCache.md" }, + { + "title": "QueryObserver", + "path": "/reference/QueryObserver", + "editUrl": "/reference/QueryObserver.md" + }, + { + "title": "QueriesObserver", + "path": "/reference/QueriesObserver", + "editUrl": "/reference/QueriesObserver.md" + }, { "title": "QueryErrorResetBoundary", "path": "/reference/QueryErrorResetBoundary", diff --git a/docs/src/pages/guides/default-query-function.md b/docs/src/pages/guides/default-query-function.md index b23248eeb4..a001210b39 100644 --- a/docs/src/pages/guides/default-query-function.md +++ b/docs/src/pages/guides/default-query-function.md @@ -13,9 +13,9 @@ const defaultQueryFn = async key => { } // provide the default query function to your app with defaultOptions -const cache = new QueryCache() -const client = new QueryClient({ - cache, +const queryCache = new QueryCache() +const queryClient = new QueryClient({ + queryCache, defaultOptions: { queries: { queryFn: defaultQueryFn, @@ -25,7 +25,7 @@ const client = new QueryClient({ function App() { return ( - + ) diff --git a/docs/src/pages/guides/infinite-queries.md b/docs/src/pages/guides/infinite-queries.md index a341341c81..254392bd48 100644 --- a/docs/src/pages/guides/infinite-queries.md +++ b/docs/src/pages/guides/infinite-queries.md @@ -88,7 +88,7 @@ function Projects() { ## What happens when an infinite query needs to be refetched? -When an infinite query becomes `stale` and needs to be refetched, each group is fetched `sequentially`, starting from the first one. This ensures that even if the underlying data is mutated we're not using stale cursors and potentially getting duplicates or skipping records. If an infinite query's results are ever removed from the cache, the pagination restarts at the initial state with only the initial group being requested. +When an infinite query becomes `stale` and needs to be refetched, each group is fetched `sequentially`, starting from the first one. This ensures that even if the underlying data is mutated we're not using stale cursors and potentially getting duplicates or skipping records. If an infinite query's results are ever removed from the queryCache, the pagination restarts at the initial state with only the initial group being requested. ## What if I need to pass custom information to my query function? diff --git a/docs/src/pages/guides/initial-query-data.md b/docs/src/pages/guides/initial-query-data.md index f8cd6fae59..ffa4aed167 100644 --- a/docs/src/pages/guides/initial-query-data.md +++ b/docs/src/pages/guides/initial-query-data.md @@ -55,20 +55,20 @@ function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch('/todos'), { initialData: () => { // Use a todo from the 'todos' query as the initial data for this todo query - return client.getQueryData('todos')?.find(d => d.id === todoId) + return queryClient.getQueryData('todos')?.find(d => d.id === todoId) }, }) } ``` -Most of the time, this pattern works well, but if the source query you're using to look up the initial data from is old, you may not want to use the data at all and just fetch from the server. To make this decision easier, you can use the `client.getQueryState` method instead to get more information about the source query, including a `state.updatedAt` timestamp you can use to decide if the query is "fresh" enough for your needs: +Most of the time, this pattern works well, but if the source query you're using to look up the initial data from is old, you may not want to use the data at all and just fetch from the server. To make this decision easier, you can use the `queryClient.getQueryState` method instead to get more information about the source query, including a `state.updatedAt` timestamp you can use to decide if the query is "fresh" enough for your needs: ```js function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch('/todos'), { initialData: () => { // Get the query state - const state = client.getQueryState('todos') + const state = queryClient.getQueryState('todos') // If the query exists and has data that is no older than 10 seconds... if (state && Date.now() - state.updatedAt <= 10 * 1000) { diff --git a/docs/src/pages/guides/invalidations-from-mutations.md b/docs/src/pages/guides/invalidations-from-mutations.md index 81a7f4e1f9..c882345dd0 100644 --- a/docs/src/pages/guides/invalidations-from-mutations.md +++ b/docs/src/pages/guides/invalidations-from-mutations.md @@ -16,13 +16,13 @@ When a successful `postTodo` mutation happens, we likely want all `todos` querie ```js import { useMutation, useQueryClient } from 'react-query' -const client = useQueryClient() +const queryClient = useQueryClient() // When this mutation succeeds, invalidate any queries with the `todos` or `reminders` query key const mutation = useMutation(addTodo, { onSuccess: () => { - client.invalidateQueries('todos') - client.invalidateQueries('reminders') + queryClient.invalidateQueries('todos') + queryClient.invalidateQueries('reminders') }, }) ``` diff --git a/docs/src/pages/guides/migrating-to-react-query-3.md b/docs/src/pages/guides/migrating-to-react-query-3.md index 69d4a0c005..feabc0d892 100644 --- a/docs/src/pages/guides/migrating-to-react-query-3.md +++ b/docs/src/pages/guides/migrating-to-react-query-3.md @@ -9,8 +9,8 @@ This article explains how to migrate your application to React Query 3. ### QueryClient -The `QueryCache` has been split into a `QueryClient` and a `QueryCache`. -The `QueryCache` contains all cached queries and the `QueryClient` can be used to interact with a cache. +The `QueryCache` has been split into a `QueryClient`, `QueryCache` and `MutationCache`. +The `QueryCache` contains all queries, the `MutationCache` contains all mutations, and the `QueryClient` can be used to set configuration and to interact with them. This has some benefits: @@ -25,11 +25,17 @@ Use the `QueryClientProvider` component to connect a `QueryClient` to your appli ```js import { QueryClient, QueryClientProvider, QueryCache } from 'react-query' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +// Create query cache +const queryCache = new QueryCache() + +// Create mutation cache (can be omitted to reduce file size when not using mutations) +const mutationCache = new MutationCache() + +// Create client +const queryClient = new QueryClient({ queryCache, mutationCache }) function App() { - return ... + return ... } ``` @@ -42,10 +48,10 @@ import { useCallback } from 'react' import { useQueryClient } from 'react-query' function Todo() { - const client = useQueryClient() + const queryClient = useQueryClient() const onClickButton = useCallback(() => { - client.invalidateQueries('posts') + queryClient.invalidateQueries('posts') }, [client]) return @@ -57,8 +63,8 @@ function Todo() { The `ReactQueryConfigProvider` component has been removed. Default options for queries and mutations can now be specified in `QueryClient`: ```js -const client = new QueryClient({ - cache, +const queryClient = new QueryClient({ + queryCache, defaultOptions: { queries: { staleTime: Infinity, @@ -157,6 +163,9 @@ mutate('todo', { onError: error => { console.error(error) }, + onSettled: () => { + console.log('settled) + }, }) ``` @@ -170,9 +179,14 @@ try { console.log(data) } catch (error) { console.error(error) +} finally { + console.log('settled) } ``` +Callbacks passed to the `mutate` or `mutateAsync` functions will now override the callbacks defined on `useMutation`. +The `mutateAsync` function can be used to compose side effects. + ### Query object syntax The object syntax has been collapsed: @@ -195,17 +209,17 @@ useQuery({ ### queryCache.prefetchQuery() -The `client.prefetchQuery()` method should now only be used for prefetching scenarios where the result is not relevant. +The `queryClient.prefetchQuery()` method should now only be used for prefetching scenarios where the result is not relevant. -Use the `client.fetchQueryData()` method to get the query data or error: +Use the `queryClient.fetchQueryData()` method to get the query data or error: ```js // Prefetch a query: -await client.prefetchQuery('posts', fetchPosts) +await queryClient.prefetchQuery('posts', fetchPosts) // Fetch a query: try { - const data = await client.fetchQueryData('posts', fetchPosts) + const data = await queryClient.fetchQueryData('posts', fetchPosts) } catch (error) { // Error handling } @@ -237,7 +251,7 @@ The `queryCache.getQueries()` method has been replaced by `cache.findAll()`. ### queryCache.isFetching -The `queryCache.isFetching` property has been replaced by `client.isFetching()`. +The `queryCache.isFetching` property has been replaced by `queryClient.isFetching()`. ### QueryOptions.enabled @@ -336,12 +350,12 @@ function Overview() { } ``` -#### client.watchQuery() +#### QueryObserver -The `client.watchQuery()` method can be used to create and/or watch a query: +A `QueryObserver` can be used to create and/or watch a query: ```js -const observer = client.watchQuery('posts') +const observer = new QueryObserver(queryClient, { queryKey: 'posts' }) const unsubscribe = observer.subscribe(result => { console.log(result) @@ -349,12 +363,12 @@ const unsubscribe = observer.subscribe(result => { }) ``` -#### client.watchQueries() +#### QueriesObserver -The `client.watchQueries()` method can be used to create and/or watch multiple queries: +A `QueriesObserver` can be used to create and/or watch multiple queries: ```js -const observer = client.watchQueries([ +const observer = new QueriesObserver(queryClient, [ { queryKey: ['post', 1], queryFn: fetchPost }, { queryKey: ['post', 2], queryFn: fetchPost }, ]) @@ -365,18 +379,30 @@ const unsubscribe = observer.subscribe(result => { }) ``` -## `client.setQueryDefaults` +## `queryClient.setQueryDefaults` -The `client.setQueryDefaults()` method to set default options for a specific query. If the query does not exist yet it will create it. +The `queryClient.setQueryDefaults()` method can be used to set default options for specific queries: ```js -client.setQueryDefaults('posts', fetchPosts) +queryClient.setQueryDefaults('posts', { queryFn: fetchPosts }) function Component() { const { data } = useQuery('posts') } ``` +## `queryClient.setMutationDefaults` + +The `queryClient.setMutationDefaults()` method can be used to set default options for specific mutations: + +```js +queryClient.setMutationDefaults('addPost', { mutationFn: addPost }) + +function Component() { + const { mutate } = useMutation('addPost') +} +``` + #### useIsFetching() The `useIsFetching()` hook now accepts filters which can be used to for example only show a spinner for certain type of queries: diff --git a/docs/src/pages/guides/mutations.md b/docs/src/pages/guides/mutations.md index 07e138d3f7..83d320c382 100644 --- a/docs/src/pages/guides/mutations.md +++ b/docs/src/pages/guides/mutations.md @@ -51,7 +51,7 @@ Beyond those primary state, more information is available depending on the state In the example above, you also saw that you can pass variables to your mutations function by calling the `mutate` function with a **single variable or object**. -Even with just variables, mutations aren't all that special, but when used with the `onSuccess` option, the [Query Client's `invalidateQueries` method](../reference/QueryClient#clientinvalidatequeries) and the [Query Client's `setQueryData` method](../reference/QueryClient#clientsetquerydata), mutations become a very powerful tool. +Even with just variables, mutations aren't all that special, but when used with the `onSuccess` option, the [Query Client's `invalidateQueries` method](../reference/QueryClient#queryclientinvalidatequeries) and the [Query Client's `setQueryData` method](../reference/QueryClient#queryclientsetquerydata), mutations become a very powerful tool. > IMPORTANT: The `mutate` function is an asynchronous function, which means you cannot use it directly in an event callback. If you need to access the event in `onSubmit` you need to wrap `mutate` in another function. This is due to [React event pooling](https://reactjs.org/docs/events.html#event-pooling). @@ -120,18 +120,12 @@ useMutation(addTodo, { onMutate: variables => { // A mutation is about to happen! - // Optionally return a context object with a rollback function - return { - rollback: () => { - // do some rollback logic - }, - } + // Optionally return a context containing data to use when for example rolling back + return { id: 1 } }, onError: (error, variables, context) => { // An error happened! - if (context.rollback) { - context.rollback() - } + console.log(`rolling back optimistic update with id ${context.id}`) }, onSuccess: (data, variables, context) => { // Boom baby! @@ -155,41 +149,37 @@ useMutation(addTodo, { }) ``` -You might find that you want to **add additional side-effects** to some of the `useMutation` lifecycle at the time of calling `mutate`. To do that, you can provide any of the same callback options to the `mutate` function after your mutation variable. Supported option overrides include: - -- `onSuccess` - Will be fired after the `useMutation`-level `onSuccess` handler -- `onError` - Will be fired after the `useMutation`-level `onError` handler -- `onSettled` - Will be fired after the `useMutation`-level `onSettled` handler +You might find that you want to **trigger different callbacks** then the ones defined on `useMutation` when calling `mutate`. To do that, you can provide any of the same callback options to the `mutate` function after your mutation variable. Supported overrides include: `onSuccess`, `onError` and `onSettled`. ```js useMutation(addTodo, { onSuccess: (data, variables, context) => { - // I will fire first + // I will not fire }, onError: (error, variables, context) => { - // I will fire first + // I will not fire }, onSettled: (data, error, variables, context) => { - // I will fire first + // I will not fire }, }) mutate(todo, { onSuccess: (data, variables, context) => { - // I will fire second! + // I will fire instead! }, onError: (error, variables, context) => { - // I will fire second! + // I will fire instead! }, onSettled: (data, error, variables, context) => { - // I will fire second! + // I will fire instead! }, }) ``` ## Promises -Use `mutateAsync` instead of `mutate` to get a promise which will resolve on success or throw on an error: +Use `mutateAsync` instead of `mutate` to get a promise which will resolve on success or throw on an error. This can for example be used to compose side effects. ```js const mutation = useMutation(addTodo) @@ -199,5 +189,19 @@ try { console.log(todo) } catch (error) { console.error(error) +} finally { + console.log('done') } ``` + +## Retry + +By default React Query will not retry a mutation on error, but it is possible with the `retry` option: + +```js +const mutation = useMutation(addTodo, { + retry: 3, +}) +``` + +If mutations fail because the device is offline, they will be retried in the same order when the device reconnects. diff --git a/docs/src/pages/guides/optimistic-updates.md b/docs/src/pages/guides/optimistic-updates.md index 43d9e3ab5b..031802478c 100644 --- a/docs/src/pages/guides/optimistic-updates.md +++ b/docs/src/pages/guides/optimistic-updates.md @@ -10,30 +10,30 @@ To do this, `useMutation`'s `onMutate` handler option allows you to return a val ## Updating a list of todos when adding a new todo ```js -const client = useQueryClient() +const queryClient = useQueryClient() useMutation(updateTodo, { // When mutate is called: onMutate: async newTodo => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) - await client.cancelQueries('todos') + await queryClient.cancelQueries('todos') // Snapshot the previous value - const previousTodos = client.getQueryData('todos') + const previousTodos = queryClient.getQueryData('todos') // Optimistically update to the new value - client.setQueryData('todos', old => [...old, newTodo]) + queryClient.setQueryData('todos', old => [...old, newTodo]) // Return a context object with the snapshotted value return { previousTodos } }, // If the mutation fails, use the context returned from onMutate to roll back onError: (err, newTodo, context) => { - client.setQueryData('todos', context.previousTodos) + queryClient.setQueryData('todos', context.previousTodos) }, // Always refetch after error or success: onSettled: () => { - client.invalidateQueries('todos') + queryClient.invalidateQueries('todos') }, }) ``` @@ -45,28 +45,27 @@ useMutation(updateTodo, { // When mutate is called: onMutate: async newTodo => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) - await client.cancelQueries(['todos', newTodo.id]) + await queryClient.cancelQueries(['todos', newTodo.id]) // Snapshot the previous value - const previousTodo = client.getQueryData(['todos', newTodo.id]) + const previousTodo = queryClient.getQueryData(['todos', newTodo.id]) // Optimistically update to the new value - client.setQueryData(['todos', newTodo.id], newTodo) + queryClient.setQueryData(['todos', newTodo.id], newTodo) - // Return a context object containing a function to rollback - return { - rollback: () => { - client.setQueryData(['todos', newTodo.id], previousTodo) - }, - } + // Return a context with the previous and new todo + return { previousTodo, newTodo } }, - // If the mutation fails, use the rollback function we returned above + // If the mutation fails, use the context we returned above onError: (err, newTodo, context) => { - context.rollback() + queryClient.setQueryData( + ['todos', context.newTodo.id], + context.previousTodo + ) }, // Always refetch after error or success: onSettled: newTodo => { - client.invalidateQueries(['todos', newTodo.id]) + queryClient.invalidateQueries(['todos', newTodo.id]) }, }) ``` @@ -78,7 +77,7 @@ useMutation(updateTodo, { // ... onSettled: (newTodo, error, variables, context) => { if (error) { - context.rollback() + // do something } }, }) diff --git a/docs/src/pages/guides/prefetching.md b/docs/src/pages/guides/prefetching.md index 31801ecdfe..119dcb4e40 100644 --- a/docs/src/pages/guides/prefetching.md +++ b/docs/src/pages/guides/prefetching.md @@ -8,7 +8,7 @@ If you're lucky enough, you may know enough about what your users will do to be ```js const prefetchTodos = async () => { // The results of this query will be cached like a normal query - await client.prefetchQuery('todos', fetchTodos) + await queryClient.prefetchQuery('todos', fetchTodos) } ``` @@ -21,5 +21,5 @@ const prefetchTodos = async () => { Alternatively, if you already have the data for your query synchronously available, you don't need to prefetch it. You can just use the [Query Client's `setQueryData` method](../api/#queryclientesetquerydata) to directly add or update a query's cached result by key. ```js -client.setQueryData('todos', todos) +queryClient.setQueryData('todos', todos) ``` diff --git a/docs/src/pages/guides/query-filters.md b/docs/src/pages/guides/query-filters.md index 8dfc57a51c..df20c61b57 100644 --- a/docs/src/pages/guides/query-filters.md +++ b/docs/src/pages/guides/query-filters.md @@ -7,16 +7,16 @@ Some methods within React Query accept a `QueryFilters` object. A query filter i ```js // Cancel all queries -await client.cancelQueries() +await queryClient.cancelQueries() // Remove all inactive queries -client.removeQueries('posts', { inactive: true }) +queryClient.removeQueries('posts', { inactive: true }) // Refetch all active queries -await client.refetchQueries({ active: true }) +await queryClient.refetchQueries({ active: true }) // Refetch all active queries that begin with `post` in the key -await client.refetchQueries('posts', { active: true }) +await queryClient.refetchQueries('posts', { active: true }) ``` A query filter object supports the following properties: @@ -32,9 +32,6 @@ A query filter object supports the following properties: - `stale?: boolean` - When set to `true` it will match stale queries. - When set to `false` it will match fresh queries. -- `fresh?: boolean` - - When set to `true` it will match fresh queries. - - When set to `false` it will match stale queries. - `fetching?: boolean` - When set to `true` it will match queries that are currently fetching. - When set to `false` it will match queries that are not fetching. diff --git a/docs/src/pages/guides/query-invalidation.md b/docs/src/pages/guides/query-invalidation.md index de1567dc1f..0a2531f961 100644 --- a/docs/src/pages/guides/query-invalidation.md +++ b/docs/src/pages/guides/query-invalidation.md @@ -7,9 +7,9 @@ Waiting for queries to become stale before they are fetched again doesn't always ```js // Invalidate every query in the cache -client.invalidateQueries() +queryClient.invalidateQueries() // Invalidate every query with a key that starts with `todos` -client.invalidateQueries('todos') +queryClient.invalidateQueries('todos') ``` > Note: Where other libraries that use normalized caches would attempt to update local queries with the new data either imperatively or via schema inference, React Query gives you the tools to avoid the manual labor that comes with maintaining normalized caches and instead prescribes **targeted invalidation, background-refetching and ultimately atomic updates**. @@ -29,9 +29,9 @@ In this example, we can use the `todos` prefix to invalidate any queries that st import { useQuery, useQueryClient } from 'react-query' // Get QueryClient from the context -const client = useQueryClient() +const queryClient = useQueryClient() -client.invalidateQueries('todos') +queryClient.invalidateQueries('todos') // Both queries below will be invalidated const todoListQuery = useQuery('todos', fetchTodoList) @@ -41,7 +41,7 @@ const todoListQuery = useQuery(['todos', { page: 1 }], fetchTodoList) You can even invalidate queries with specific variables by passing a more specific query key to the `invalidateQueries` method: ```js -client.invalidateQueries(['todos', { type: 'done' }]) +queryClient.invalidateQueries(['todos', { type: 'done' }]) // The query below will be invalidated const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList) @@ -53,7 +53,7 @@ const todoListQuery = useQuery('todos', fetchTodoList) The `invalidateQueries` API is very flexible, so even if you want to **only** invalidate `todos` queries that don't have any more variables or subkeys, you can pass an `exact: true` option to the `invalidateQueries` method: ```js -client.invalidateQueries('todos', { exact: true }) +queryClient.invalidateQueries('todos', { exact: true }) // The query below will be invalidated const todoListQuery = useQuery(['todos'], fetchTodoList) @@ -65,7 +65,7 @@ const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList) If you find yourself wanting **even more** granularity, you can pass a predicate function to the `invalidateQueries` method. This function will receive each `Query` instance from the query cache and allow you to return `true` or `false` for whether you want to invalidate that query: ```js -client.invalidateQueries({ +queryClient.invalidateQueries({ predicate: query => query.queryKey[0] === 'todos' && query.queryKey[1]?.version >= 10, }) diff --git a/docs/src/pages/guides/query-retries.md b/docs/src/pages/guides/query-retries.md index d4aa0abeef..6553a22f2b 100644 --- a/docs/src/pages/guides/query-retries.md +++ b/docs/src/pages/guides/query-retries.md @@ -31,9 +31,9 @@ The default `retryDelay` is set to double (starting at `1000`ms) with each attem // Configure for all queries import { QueryCache, QueryClient, QueryClientProvider } from 'react-query' -const cache = new QueryCache() -const client = new QueryClient({ - cache, +const queryCache = new QueryCache() +const queryClient = new QueryClient({ + queryCache, defaultOptions: { queries: { retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000), @@ -42,7 +42,7 @@ const client = new QueryClient({ }) function App() { - return ... + return ... } ``` diff --git a/docs/src/pages/guides/ssr.md b/docs/src/pages/guides/ssr.md index 7bdd706915..a5b312b7a1 100644 --- a/docs/src/pages/guides/ssr.md +++ b/docs/src/pages/guides/ssr.md @@ -3,7 +3,7 @@ id: ssr title: SSR --- -React Query supports two ways of prefetching data on the server and passing that to the client. +React Query supports two ways of prefetching data on the server and passing that to the queryClient. - Prefetch the data yourself and pass it in as `initialData` - Quick to set up for simple cases @@ -45,7 +45,7 @@ The setup is minimal and this can be a quick solution for some cases, but there ### Using Hydration -React Query supports prefetching multiple queries on the server in Next.js and then _dehydrating_ those queries to the client. This means the server can prerender markup that is immediately available on page load and as soon as JS is available, React Query can upgrade or _hydrate_ those queries with the full functionality of the library. This includes refetching those queries on the client if they have become stale since the time they were rendered on the server. +React Query supports prefetching multiple queries on the server in Next.js and then _dehydrating_ those queries to the queryClient. This means the server can prerender markup that is immediately available on page load and as soon as JS is available, React Query can upgrade or _hydrate_ those queries with the full functionality of the library. This includes refetching those queries on the client if they have become stale since the time they were rendered on the server. To support caching queries on the server and set up hydration: @@ -58,12 +58,12 @@ To support caching queries on the server and set up hydration: import { QueryCache, QueryClient, QueryClientProvider } from 'react-query' import { Hydrate } from 'react-query/hydration' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) export default function MyApp({ Component, pageProps }) { return ( - + @@ -84,10 +84,10 @@ import { QueryCache, QueryClient, useQuery } from 'react-query' import { dehydrate } from 'react-query/hydration' export async function getStaticProps() { - const cache = new QueryCache() - const client = new QueryClient({ cache }) + const queryCache = new QueryCache() + const queryClient = new QueryClient({ queryCache }) - await client.prefetchQuery('posts', getPosts) + await queryClient.prefetchQuery('posts', getPosts) return { props: { @@ -109,7 +109,7 @@ function Posts() { } ``` -As demonstrated, it's fine to prefetch some queries and let others fetch on the client. This means you can control what content server renders or not by adding or removing `prefetchQuery` for a specific query. +As demonstrated, it's fine to prefetch some queries and let others fetch on the queryClient. This means you can control what content server renders or not by adding or removing `prefetchQuery` for a specific query. ## Using Other Frameworks or Custom SSR Frameworks @@ -119,10 +119,9 @@ This guide is at-best, a high level overview of how SSR with React Query should ### On the Server -- Create a new `QueryCache` instance - Create a new `QueryClient` instance - Using the client, prefetch any data you need -- Dehydrate the cache +- Dehydrate the client - Render your app with the client provider and also **using the dehydrated state. This is extremely important! You must render both server and client using the same dehydrated state to ensure hydration on the client produces the exact same markup as the server.** - Serialize and embed the dehydrated cache to be sent to the client with the HTML @@ -132,13 +131,13 @@ This guide is at-best, a high level overview of how SSR with React Query should import { QueryCache, QueryClient, QueryClientProvider } from 'react-query' import { dehydrate, Hydrate } from 'react-query/hydration' -const cache = new QueryCache() -const client = new QueryClient({ cache }) -await client.prefetchQuery('key', fn) -const dehydratedState = dehydrate(cache) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) +await queryClient.prefetchQuery('key', fn) +const dehydratedState = dehydrate(client) const html = ReactDOM.renderToString( - + @@ -150,9 +149,7 @@ res.send(`
${html}
@@ -170,13 +167,13 @@ res.send(` import { QueryCache, QueryClient, QueryClientProvider } from 'react-query' import { Hydrate } from 'react-query/hydration' -const dehydratedState = JSON.parse(window.__REACT_QUERY_INITIAL_QUERIES__) +const dehydratedState = JSON.parse(window.__REACT_QUERY_STATE__) -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) ReactDOM.hydrate( - + @@ -188,7 +185,7 @@ ReactDOM.hydrate( ### Only successful queries are included in dehydration -Any query with an error is automatically excluded from dehydration. This means that the default behaviour is to pretend these queries were never loaded on the server, usually showing a loading state instead, and retrying the queries on the client. This happens regardless of error. +Any query with an error is automatically excluded from dehydration. This means that the default behaviour is to pretend these queries were never loaded on the server, usually showing a loading state instead, and retrying the queries on the queryClient. This happens regardless of error. Sometimes this behavior is not desirable, maybe you want to render an error page with a correct status code instead on certain errors or queries. In those cases, use `fetchQueryData` and catch any errors to handle those manually. diff --git a/docs/src/pages/guides/suspense.md b/docs/src/pages/guides/suspense.md index 0c60ded60f..f3598c4ddf 100644 --- a/docs/src/pages/guides/suspense.md +++ b/docs/src/pages/guides/suspense.md @@ -13,9 +13,9 @@ Global configuration: // Configure for all queries import { QueryCache, QueryClient, QueryClientProvider } from 'react-query' -const cache = new QueryCache() -const client = new QueryClient({ - cache, +const queryCache = new QueryCache() +const queryClient = new QueryClient({ + queryCache, defaultOptions: { queries: { suspense: true, @@ -25,7 +25,7 @@ const client = new QueryClient({ function Root() { return ( - + ) diff --git a/docs/src/pages/guides/updates-from-mutation-responses.md b/docs/src/pages/guides/updates-from-mutation-responses.md index 67493d2745..d9e78e63dd 100644 --- a/docs/src/pages/guides/updates-from-mutation-responses.md +++ b/docs/src/pages/guides/updates-from-mutation-responses.md @@ -3,13 +3,13 @@ id: updates-from-mutation-responses title: Updates from Mutation Responses --- -When dealing with mutations that **update** objects on the server, it's common for the new object to be automatically returned in the response of the mutation. Instead of refetching any queries for that item and wasting a network call for data we already have, we can take advantage of the object returned by the mutation function and update the existing query with the new data immediately using the [Query Client's `setQueryData`](../reference/QueryClient#clientsetquerydata) method: +When dealing with mutations that **update** objects on the server, it's common for the new object to be automatically returned in the response of the mutation. Instead of refetching any queries for that item and wasting a network call for data we already have, we can take advantage of the object returned by the mutation function and update the existing query with the new data immediately using the [Query Client's `setQueryData`](../reference/QueryClient#queryclientsetquerydata) method: ```js -const client = useQueryClient() +const queryClient = useQueryClient() const mutation = useMutation(editTodo, { - onSuccess: data => client.setQueryData(['todo', { id: 5 }], data), + onSuccess: data => queryClient.setQueryData(['todo', { id: 5 }], data), }) mutation.mutate({ @@ -27,12 +27,12 @@ create a custom hook like this: ```js const useMutateTodo = () => { - const client = useQueryClient() + const queryClient = useQueryClient() return useMutation(editTodo, { // Notice the second argument is the variables object that the `mutate` function receives onSuccess: (data, variables) => { - client.setQueryData(['todo', { id: variables.id }], data) + queryClient.setQueryData(['todo', { id: variables.id }], data) }, }) } diff --git a/docs/src/pages/guides/window-focus-refetching.md b/docs/src/pages/guides/window-focus-refetching.md index 2b6695cd37..cbab2ba6a4 100644 --- a/docs/src/pages/guides/window-focus-refetching.md +++ b/docs/src/pages/guides/window-focus-refetching.md @@ -9,7 +9,7 @@ If a user leaves your application and returns to stale data, **React Query autom ```js // -const client = new QueryClient({ +const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnWindowFocus: false, @@ -18,7 +18,7 @@ const client = new QueryClient({ }) function App() { - return ... + return ... } ``` @@ -30,10 +30,10 @@ useQuery('todos', fetchTodos, { refetchOnWindowFocus: false }) ## Custom Window Focus Event -In rare circumstances, you may want to manage your own window focus events that trigger React Query to revalidate. To do this, React Query provides a `setFocusHandler` function that supplies you the callback that should be fired when the window is focused and allows you to set up your own events. When calling `setFocusHandler`, the previously set handler is removed (which in most cases will be the default handler) and your new handler is used instead. For example, this is the default handler: +In rare circumstances, you may want to manage your own window focus events that trigger React Query to revalidate. To do this, React Query provides a `focusManager.setHandler` function that supplies you the callback that should be fired when the window is focused and allows you to set up your own events. When calling `focusManager.setHandler`, the previously set handler is removed (which in most cases will be the default handler) and your new handler is used instead. For example, this is the default handler: ```js -setFocusHandler(handleFocus => { +focusManager.setHandler(handleFocus => { // Listen to visibillitychange and focus if (typeof window !== 'undefined' && window.addEventListener) { window.addEventListener('visibilitychange', handleFocus, false) @@ -53,10 +53,10 @@ setFocusHandler(handleFocus => { A great use-case for replacing the focus handler is that of iframe events. Iframes present problems with detecting window focus by both double-firing events and also firing false-positive events when focusing or using iframes within your app. If you experience this, you should use an event handler that ignores these events as much as possible. I recommend [this one](https://gist.github.com/tannerlinsley/1d3a2122332107fcd8c9cc379be10d88)! It can be set up in the following way: ```js -import { setFocusHandler } from 'react-query' +import { focusManager } from 'react-query' import onWindowFocus from './onWindowFocus' // The gist above -setFocusHandler(onWindowFocus) // Boom! +focusManager.setHandler(onWindowFocus) // Boom! ``` ## Managing Focus in React Native @@ -64,10 +64,10 @@ setFocusHandler(onWindowFocus) // Boom! Instead of event listeners on `window`, React Native provides focus information through the [`AppState` module](https://reactnative.dev/docs/appstate#app-states). You can use the `AppState` "change" event to trigger an update when the app state changes to "active": ```js -import { setFocusHandler } from 'react-query' +import { focusManager } from 'react-query' import { AppState } from 'react-native' -setFocusHandler(handleFocus => { +focusManager.setHandler(handleFocus => { const handleAppStateChange = appState => { if (appState === 'active') { handleFocus() diff --git a/docs/src/pages/overview.md b/docs/src/pages/overview.md index ef54ff40b3..0dde05b09d 100644 --- a/docs/src/pages/overview.md +++ b/docs/src/pages/overview.md @@ -54,12 +54,12 @@ import { QueryClientProvider, } from 'react-query' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) export default function App() { return ( - + ) diff --git a/docs/src/pages/quick-start.md b/docs/src/pages/quick-start.md index 536aaa2a55..f00cf5a9ce 100644 --- a/docs/src/pages/quick-start.md +++ b/docs/src/pages/quick-start.md @@ -21,15 +21,15 @@ import { import { getTodos, postTodo } from '../my-api' // Create a cache -const cache = new QueryCache() +const queryCache = new QueryCache() // Create a client -const client = new QueryClient({ cache }) +const queryClient = new QueryClient({ queryCache }) function App() { return ( // Provide the client to your App - + ) @@ -37,7 +37,7 @@ function App() { function Todos() { // Access the client - const client = useQueryClient() + const queryClient = useQueryClient() // Queries const query = useQuery('todos', getTodos) @@ -46,7 +46,7 @@ function Todos() { const mutation = useMutation(postTodo, { onSuccess: () => { // Invalidate and refetch - client.invalidateQueries('todos') + queryClient.invalidateQueries('todos') }, }) diff --git a/docs/src/pages/reference/QueriesObserver.md b/docs/src/pages/reference/QueriesObserver.md new file mode 100644 index 0000000000..8ea31db44d --- /dev/null +++ b/docs/src/pages/reference/QueriesObserver.md @@ -0,0 +1,24 @@ +--- +id: QueriesObserver +title: QueriesObserver +--- + +## `QueriesObserver` + +The `QueriesObserver` can be used to observe multiple queries. + +```js +const observer = new QueriesObserver(queryClient, [ + { queryKey: ['post', 1], queryFn: fetchPost }, + { queryKey: ['post', 2], queryFn: fetchPost }, +]) + +const unsubscribe = observer.subscribe(result => { + console.log(result) + unsubscribe() +}) +``` + +**Options** + +The options for the `QueriesObserver` are exactly the same as those of [`useQueries`](#usequeries). diff --git a/docs/src/pages/reference/QueryCache.md b/docs/src/pages/reference/QueryCache.md index 46fd263bf2..6818128d90 100644 --- a/docs/src/pages/reference/QueryCache.md +++ b/docs/src/pages/reference/QueryCache.md @@ -10,25 +10,25 @@ The `QueryCache` is the storage mechanism for React Query. It stores all of the ```js import { QueryCache } from 'react-query' -const cache = new QueryCache() -const query = cache.find('posts') +const queryCache = new QueryCache() +const query = queryCache.find('posts') ``` Its available methods are: -- [`find`](#cachefind) -- [`findAll`](#cachefindall) -- [`subscribe`](#cachesubscribe) -- [`clear`](#cacheclear) +- [`find`](#querycachefind) +- [`findAll`](#querycachefindall) +- [`subscribe`](#querycachesubscribe) +- [`clear`](#querycacheclear) -## `cache.find` +## `queryCache.find` `find` is a slightly more advanced synchronous method that can be used to get an existing query instance from the cache. This instance not only contains **all** the state for the query, but all of the instances, and underlying guts of the query as well. If the query does not exist, `undefined` will be returned. > Note: This is not typically needed for most applications, but can come in handy when needing more information about a query in rare scenarios (eg. Looking at the query.state.updatedAt timestamp to decide whether a query is fresh enough to be used as an initial value) ```js -const query = cache.find(queryKey) +const query = queryCache.find(queryKey) ``` **Options** @@ -41,14 +41,14 @@ const query = cache.find(queryKey) - `Query` - The query instance from the cache -## `cache.findAll` +## `queryCache.findAll` `findAll` is even more advanced synchronous method that can be used to get existing query instances from the cache that partially match query key. If queries do not exist, empty array will be returned. > Note: This is not typically needed for most applications, but can come in handy when needing more information about a query in rare scenarios ```js -const queries = cache.findAll(queryKey) +const queries = queryCache.findAll(queryKey) ``` **Options** @@ -61,7 +61,7 @@ const queries = cache.findAll(queryKey) - `Query[]` - Query instances from the cache -## `cache.subscribe` +## `queryCache.subscribe` The `subscribe` method can be used to subscribe to the query cache as a whole and be informed of safe/known updates to the cache like query states changing or queries being updated, added or removed @@ -70,13 +70,13 @@ const callback = query => { console.log(query) } -const unsubscribe = cache.subscribe(callback) +const unsubscribe = queryCache.subscribe(callback) ``` **Options** - `callback: (query?: Query) => void` - - This function will be called with the query cache any time it is updated via its tracked update mechanisms (eg, `query.setState`, `client.removeQueries`, etc). Out of scope mutations to the cache are not encouraged and will not fire subscription callbacks + - This function will be called with the query cache any time it is updated via its tracked update mechanisms (eg, `query.setState`, `queryClient.removeQueries`, etc). Out of scope mutations to the cache are not encouraged and will not fire subscription callbacks - Additionally, for updates to the cache triggered by a specific query, the `query` will be passed as first argument to the callback **Returns** @@ -84,10 +84,10 @@ const unsubscribe = cache.subscribe(callback) - `unsubscribe: Function => void` - This function will unsubscribe the callback from the query cache. -## `cache.clear` +## `queryCache.clear` The `clear` method can be used to clear the cache entirely and start fresh. ```js -cache.clear() +queryCache.clear() ``` diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index 2ba5b9ec68..5b53990707 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -10,9 +10,9 @@ The `QueryClient` can be used to interact with a cache: ```js import { QueryClient, QueryCache } from 'react-query' -const cache = new QueryCache() -const client = new QueryClient({ - cache, +const queryCache = new QueryCache() +const queryClient = new QueryClient({ + queryCache, defaultOptions: { queries: { staleTime: Infinity, @@ -20,36 +20,36 @@ const client = new QueryClient({ }, }) -await client.prefetchQuery('posts', fetchPosts) +await queryClient.prefetchQuery('posts', fetchPosts) ``` Its available methods are: -- [`fetchQueryData`](#clientfetchquerydata) -- [`prefetchQuery`](#clientprefetchquery) -- [`getQueryData`](#clientgetquerydata) -- [`setQueryData`](#clientsetquerydata) -- [`getQueryState`](#clientgetquerystate) -- [`setQueryDefaults`](#clientsetquerydefaults) -- [`refetchQueries`](#clientrefetchqueries) -- [`invalidateQueries`](#clientinvalidatequeries) -- [`cancelQueries`](#clientcancelqueries) -- [`removeQueries`](#clientremovequeries) -- [`watchQuery`](#clientwatchquery) -- [`watchQueries`](#clientwatchqueries) +- [`fetchQueryData`](#queryclientfetchquerydata) +- [`prefetchQuery`](#queryclientprefetchquery) +- [`getQueryData`](#queryclientgetquerydata) +- [`setQueryData`](#queryclientsetquerydata) +- [`getQueryState`](#queryclientgetquerystate) +- [`setQueryDefaults`](#queryclientsetquerydefaults) +- [`refetchQueries`](#queryclientrefetchqueries) +- [`invalidateQueries`](#queryclientinvalidatequeries) +- [`cancelQueries`](#queryclientcancelqueries) +- [`removeQueries`](#queryclientremovequeries) - [`isFetching`](#queryclientisfetching) -- [`getDefaultOptions`](#clientsetdefaultoptions) -- [`setDefaultOptions`](#clientgetdefaultoptions) +- [`getDefaultOptions`](#queryclientsetdefaultoptions) +- [`setDefaultOptions`](#queryclientgetdefaultoptions) **Options** -- `cache: QueryCache` +- `queryCache: QueryCache` - The query cache this client is connected to. +- `mutationCache: MutationCache` + - The mutation cache this client is connected to. - `defaultOptions: DefaultOptions` - Optional - - Define defaults for all queries and mutations using this query client. + - Define defaults for all queries and mutations using this queryClient. -## `client.fetchQueryData` +## `queryClient.fetchQueryData` `fetchQueryData` is an asynchronous method that can be used to fetch and cache a query. It will either resolve with the data or throw with the error. Use the `prefetchQuery` method if you just want to fetch a query without needing the result. @@ -59,7 +59,7 @@ If the query exists and the data is not invalidated or older than the given `sta ```js try { - const data = await client.fetchQueryData(queryKey, queryFn) + const data = await queryClient.fetchQueryData(queryKey, queryFn) } catch (error) { console.log(error) } @@ -69,7 +69,7 @@ Specify a `staleTime` to only fetch when the data is older than a certain amount ```js try { - const data = await client.fetchQueryData(queryKey, queryFn, { + const data = await queryClient.fetchQueryData(queryKey, queryFn, { staleTime: 10000, }) } catch (error) { @@ -85,18 +85,18 @@ The options for `fetchQueryData` are exactly the same as those of [`useQuery`](# - `Promise` -## `client.prefetchQuery` +## `queryClient.prefetchQuery` `prefetchQuery` is an asynchronous method that can be used to prefetch a query before it is needed or rendered with `useQuery` and friends. The method works the same as `fetchQueryData` except that is will not throw or return any data. ```js -await client.prefetchQuery(queryKey, queryFn) +await queryClient.prefetchQuery(queryKey, queryFn) ``` You can even use it with a default queryFn in your config! ```js -await client.prefetchQuery(queryKey) +await queryClient.prefetchQuery(queryKey) ``` **Options** @@ -108,12 +108,12 @@ The options for `prefetchQuery` are exactly the same as those of [`useQuery`](#u - `Promise` - A promise is returned that will either immediately resolve if no fetch is needed or after the query has been executed. It will not return any data or throw any errors. -## `client.getQueryData` +## `queryClient.getQueryData` `getQueryData` is a synchronous function that can be used to get an existing query's cached data. If the query does not exist, `undefined` will be returned. ```js -const data = client.getQueryData(queryKey) +const data = queryClient.getQueryData(queryKey) ``` **Options** @@ -126,14 +126,14 @@ const data = client.getQueryData(queryKey) - `data: TData | undefined` - The data for the cached query, or `undefined` if the query does not exist. -## `client.setQueryData` +## `queryClient.setQueryData` `setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**. > The difference between using `setQueryData` and `fetchQueryData` is that `setQueryData` is sync and assumes that you already synchronously have the data available. If you need to fetch the data asynchronously, it's suggested that you either refetch the query key or use `fetchQueryData` to handle the asynchronous fetch. ```js -client.setQueryData(queryKey, updater) +queryClient.setQueryData(queryKey, updater) ``` **Options** @@ -157,12 +157,12 @@ For convenience in syntax, you can also pass an updater function which receives setQueryData(queryKey, oldData => newData) ``` -## `client.getQueryState` +## `queryClient.getQueryState` `getQueryState` is a synchronous function that can be used to get an existing query's state. If the query does not exist, `undefined` will be returned. ```js -const state = client.getQueryState(queryKey) +const state = queryClient.getQueryState(queryKey) console.log(state.updatedAt) ``` @@ -171,12 +171,12 @@ console.log(state.updatedAt) - `queryKey?: QueryKey`: [Query Keys](../guides/query-keys) - `filters?: QueryFilters`: [Query Filters](../guides/query-filters) -## `client.setQueryDefaults` +## `queryClient.setQueryDefaults` -`setQueryDefaults` is a synchronous method to set default options for a specific query. If the query does not exist yet it will create it. +`setQueryDefaults` is a synchronous method to set default options for specific queries: ```js -client.setQueryDefaults('posts', fetchPosts) +queryClient.setQueryDefaults('posts', { queryFn: fetchPosts }) function Component() { const { data } = useQuery('posts') @@ -185,10 +185,27 @@ function Component() { **Options** -- `queryKey?: QueryKey`: [Query Keys](../guides/query-keys) -- `filters?: QueryFilters`: [Query Filters](../guides/query-filters) +- `queryKey: QueryKey`: [Query Keys](../guides/query-keys) +- `options: QueryOptions` + +## `queryClient.setMutationDefaults` + +`setMutationDefaults` is a synchronous method to set default options for specific mutations: + +```js +queryClient.setMutationDefaults('addPost', { mutationFn: addPost }) -## `client.invalidateQueries` +function Component() { + const { data } = useMutation('addPost') +} +``` + +**Options** + +- `mutationKey: string | unknown[]` +- `options: MutationOptions` + +## `queryClient.invalidateQueries` The `invalidateQueries` method can be used to invalidate and refetch single or multiple queries in the cache based on their query keys or any other functionally accessible property/state of the query. By default, all matching queries are immediately marked as invalid and active queries are refetched in the background. @@ -196,7 +213,7 @@ The `invalidateQueries` method can be used to invalidate and refetch single or m - If you **want inactive queries to refetch** as well, use the `refetchInactive: true` option ```js -await client.invalidateQueries('posts', { +await queryClient.invalidateQueries('posts', { exact, refetchActive = true, refetchInactive = false @@ -217,7 +234,7 @@ await client.invalidateQueries('posts', { - `throwOnError?: boolean` - When set to `true`, this method will throw if any of the query refetch tasks fail. -## `client.refetchQueries` +## `queryClient.refetchQueries` The `refetchQueries` method can be used to refetch queries based on certain conditions. @@ -225,16 +242,16 @@ Examples: ```js // refetch all queries: -await client.refetchQueries() +await queryClient.refetchQueries() // refetch all stale queries: -await client.refetchQueries({ stale: true }) +await queryClient.refetchQueries({ stale: true }) // refetch all active queries partially matching a query key: -await client.refetchQueries(['posts'], { active: true }) +await queryClient.refetchQueries(['posts'], { active: true }) // refetch all active queries exactly matching a query key: -await client.refetchQueries(['posts', 1], { active: true, exact: true }) +await queryClient.refetchQueries(['posts', 1], { active: true, exact: true }) ``` **Options** @@ -249,14 +266,14 @@ await client.refetchQueries(['posts', 1], { active: true, exact: true }) This function returns a promise that will resolve when all of the queries are done being refetched. By default, it **will not** throw an error if any of those queries refetches fail, but this can be configured by setting the `throwOnError` option to `true` -## `client.cancelQueries` +## `queryClient.cancelQueries` The `cancelQueries` method can be used to cancel outgoing queries based on their query keys or any other functionally accessible property/state of the query. This is most useful when performing optimistic updates since you will likely need to cancel any outgoing query refetches so they don't clobber your optimistic update when they resolve. ```js -await client.cancelQueries('posts', { exact: true }) +await queryClient.cancelQueries('posts', { exact: true }) ``` **Options** @@ -268,12 +285,12 @@ await client.cancelQueries('posts', { exact: true }) This method does not return anything -## `client.removeQueries` +## `queryClient.removeQueries` The `removeQueries` method can be used to remove queries from the cache based on their query keys or any other functionally accessible property/state of the query. ```js -client.removeQueries(queryKey, { exact: true }) +queryClient.removeQueries(queryKey, { exact: true }) ``` **Options** @@ -285,57 +302,12 @@ client.removeQueries(queryKey, { exact: true }) This method does not return anything -## `client.watchQuery` - -The `watchQuery` method returns a `QueryObserver` instance which can be used to watch a query. - -```js -const observer = client.watchQuery('posts') - -const unsubscribe = observer.subscribe(result => { - console.log(result) - unsubscribe() -}) -``` - -**Options** - -The options for `watchQuery` are exactly the same as those of [`useQuery`](#usequery). - -**Returns** - -- `QueryObserver` - -## `client.watchQueries` - -The `watchQueries` method returns a `QueriesObserver` instance to watch multiple queries. - -```js -const observer = client.watchQueries([ - { queryKey: ['post', 1], queryFn: fetchPost }, - { queryKey: ['post', 2], queryFn: fetchPost }, -]) - -const unsubscribe = observer.subscribe(result => { - console.log(result) - unsubscribe() -}) -``` - -**Options** - -The options for `watchQueries` are exactly the same as those of [`useQueries`](#usequeries). - -**Returns** - -- `QueriesObserver` - -## `client.isFetching` +## `queryClient.isFetching` This `isFetching` method returns an `integer` representing how many queries, if any, in the cache are currently fetching (including background-fetching, loading new pages, or loading more infinite query results) ```js -if (client.isFetching()) { +if (queryClient.isFetching()) { console.log('At least one query is fetching!') } ``` @@ -351,20 +323,20 @@ React Query also exports a handy [`useIsFetching`](#useisfetching) hook that wil This method returns the number of fetching queries. -## `client.getDefaultOptions` +## `queryClient.getDefaultOptions` The `getDefaultOptions` method returns the default options which have been set when creating the client or with `setDefaultOptions`. ```js -const defaultOptions = client.getDefaultOptions() +const defaultOptions = queryClient.getDefaultOptions() ``` -## `client.setDefaultOptions` +## `queryClient.setDefaultOptions` -The `setDefaultOptions` method can be used to dynamically set the default options for this client. +The `setDefaultOptions` method can be used to dynamically set the default options for this queryClient. ```js -client.setDefaultOptions({ +queryClient.setDefaultOptions({ queries: { staleTime: Infinity, }, diff --git a/docs/src/pages/reference/QueryClientProvider.md b/docs/src/pages/reference/QueryClientProvider.md index b0bfd7d085..95a0dd95c0 100644 --- a/docs/src/pages/reference/QueryClientProvider.md +++ b/docs/src/pages/reference/QueryClientProvider.md @@ -8,10 +8,10 @@ Use the `QueryClientProvider` component to connect and provide a `QueryClient` t ```js import { QueryClient, QueryClientProvider, QueryCache } from 'react-query' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) function App() { - return ... + return ... } ``` diff --git a/docs/src/pages/reference/QueryObserver.md b/docs/src/pages/reference/QueryObserver.md new file mode 100644 index 0000000000..70ccfba125 --- /dev/null +++ b/docs/src/pages/reference/QueryObserver.md @@ -0,0 +1,21 @@ +--- +id: QueryObserver +title: QueryObserver +--- + +## `QueryObserver` + +The `QueryObserver` can be used to observe and switch between queries. + +```js +const observer = new QueryObserver(queryClient, { queryKey: 'posts' }) + +const unsubscribe = observer.subscribe(result => { + console.log(result) + unsubscribe() +}) +``` + +**Options** + +The options for the `QueryObserver` are exactly the same as those of [`useQuery`](#usequery). diff --git a/docs/src/pages/reference/hydration/HydrateComp.md b/docs/src/pages/reference/hydration/HydrateComp.md index 21e0e95082..ea868b08a3 100644 --- a/docs/src/pages/reference/hydration/HydrateComp.md +++ b/docs/src/pages/reference/hydration/HydrateComp.md @@ -3,7 +3,7 @@ id: hydration/HydrateComp title: hydration/Hydrate --- -`hydration/Hydrate` adds a previously dehydrated state into the `cache` that would returned by running `useQueryCache`. If the cache already contains data, the new queries will be intelligently merged based on update timestamp. +`hydration/Hydrate` adds a previously dehydrated state into the `queryClient` that would returned by running `useQueryCache`. If the client already contains data, the new queries will be intelligently merged based on update timestamp. ```js import { Hydrate } from 'react-query/hydration' diff --git a/docs/src/pages/reference/hydration/dehydrate.md b/docs/src/pages/reference/hydration/dehydrate.md index 86defff870..0e213d23f7 100644 --- a/docs/src/pages/reference/hydration/dehydrate.md +++ b/docs/src/pages/reference/hydration/dehydrate.md @@ -8,26 +8,26 @@ title: hydration/dehydrate ```js import { dehydrate } from 'react-query/hydration' -const dehydratedState = dehydrate(cache, { - shouldDehydrate, +const dehydratedState = dehydrate(queryClient, { + shouldDehydrateQuery, }) ``` **Options** -- `cache: QueryCache` +- `client: QueryClient` - **Required** - - The `cache` that should be dehydrated + - The `queryClient` that should be dehydrated - `options: DehydrateOptions` - Optional - - `shouldDehydrate: (query: Query) => boolean` + - `shouldDehydrateQuery: (query: Query) => boolean` - This function is called for each query in the cache - Return `true` to include this query in dehydration, or `false` otherwise - - Default version only includes successful queries, do `shouldDehydrate: () => true` to include all queries + - Default version only includes successful queries, do `shouldDehydrateQuery: () => true` to include all queries **Returns** - `dehydratedState: DehydratedState` - - This includes everything that is needed to hydrate the `cache` at a later point + - This includes everything that is needed to hydrate the `queryClient` at a later point - You **should not** rely on the exact format of this response, it is not part of the public API and can change at any time - This result is not in serialized form, you need to do that yourself if desired diff --git a/docs/src/pages/reference/hydration/hydrate.md b/docs/src/pages/reference/hydration/hydrate.md index 6cf435e5c0..406f624447 100644 --- a/docs/src/pages/reference/hydration/hydrate.md +++ b/docs/src/pages/reference/hydration/hydrate.md @@ -3,22 +3,22 @@ id: hydration/hydrate title: hydration/hydrate --- -`hydrate` adds a previously dehydrated state into a `cache`. If the queries included in dehydration already exist in the cache, `hydrate` does not overwrite them. +`hydrate` adds a previously dehydrated state into a `cache`. If the queries included in dehydration already exist in the queryCache, `hydrate` does not overwrite them. ```js import { hydrate } from 'react-query/hydration' -hydrate(cache, dehydratedState, options) +hydrate(queryClient, dehydratedState, options) ``` **Options** -- `cache: QueryCache` +- `client: QueryClient` - **Required** - - The `cache` to hydrate the state into + - The `queryClient` to hydrate the state into - `dehydratedState: DehydratedState` - **Required** - - The state to hydrate into the cache + - The state to hydrate into the client - `options: HydrateOptions` - Optional - `defaultOptions: QueryOptions` diff --git a/docs/src/pages/reference/hydration/useHydrate.md b/docs/src/pages/reference/hydration/useHydrate.md index 08f5333f1a..995b051305 100644 --- a/docs/src/pages/reference/hydration/useHydrate.md +++ b/docs/src/pages/reference/hydration/useHydrate.md @@ -3,7 +3,7 @@ id: hydration/useHydrate title: hydration/useHydrate --- -`useHydrate` adds a previously dehydrated state into the `cache` that would be returned by `useQueryCache()`. If the cache already contains data, the new queries will be intelligently merged based on update timestamp. +`useHydrate` adds a previously dehydrated state into the `queryClient` that would be returned by `useQueryClient()`. If the client already contains data, the new queries will be intelligently merged based on update timestamp. ```jsx import { useHydrate } from 'react-query/hydration' diff --git a/docs/src/pages/reference/useMutation.md b/docs/src/pages/reference/useMutation.md index e465848309..004c705b7f 100644 --- a/docs/src/pages/reference/useMutation.md +++ b/docs/src/pages/reference/useMutation.md @@ -10,6 +10,7 @@ const { isError, isIdle, isLoading, + isPaused, isSuccess, mutate, mutateAsync, @@ -36,26 +37,34 @@ mutate(variables, { - **Required** - A function that performs an asynchronous task and returns a promise. - `variables` is an object that `mutate` will pass to your `mutationFn` +- `mutationKey: string` + - Optional + - A mutation key can be set to inherit defaults set with `queryClient.setMutationDefaults` or to identify the mutation in the devtools. - `onMutate: (variables: TVariables) => Promise | TContext | void` - Optional - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive - Useful to perform optimistic updates to a resource in hopes that the mutation succeeds - The value returned from this function will be passed to both the `onError` and `onSettled` functions in the event of a mutation failure and can be useful for rolling back optimistic updates. -- `onSuccess: (data: TData, variables: TVariables, context: TContext) => Promise | void` +- `onSuccess: (data: TData, variables: TVariables, context?: TContext) => Promise | void` - Optional - This function will fire when the mutation is successful and will be passed the mutation's result. - - Fires after the `mutate`-level `onSuccess` handler (if it is defined) - If a promise is returned, it will be awaited and resolved before proceeding - `onError: (err: TError, variables: TVariables, context?: TContext) => Promise | void` - Optional - This function will fire if the mutation encounters an error and will be passed the error. - - Fires after the `mutate`-level `onError` handler (if it is defined) - If a promise is returned, it will be awaited and resolved before proceeding - `onSettled: (data: TData, error: TError, variables: TVariables, context?: TContext) => Promise | void` - Optional - This function will fire when the mutation is either successfully fetched or encounters an error and be passed either the data or error - - Fires after the `mutate`-level `onSettled` handler (if it is defined) - If a promise is returned, it will be awaited and resolved before proceeding +- `retry: boolean | number | (failureCount: number, error: TError) => boolean` + - If `false`, failed mutations will not retry by default. + - If `true`, failed mutations will retry infinitely. + - If set to an `number`, e.g. `3`, failed mutations will retry until the failed mutations count meets that number. +- `retryDelay: (retryAttempt: number) => number` + - This function receives a `retryAttempt` integer and returns the delay to apply before the next attempt in milliseconds. + - A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential backoff. + - A function like `attempt => attempt * 1000` applies linear backoff. - `useErrorBoundary` - Defaults to the global query config's `useErrorBoundary` value, which is `false` - Set this to true if you want mutation errors to be thrown in the render phase and propagate to the nearest error boundary @@ -63,12 +72,11 @@ mutate(variables, { **Returns** - `mutate: (variables: TVariables, { onSuccess, onSettled, onError }) => void` - - The mutation function you can call with variables to trigger the mutation and optionally override the original mutation options. + - The mutation function you can call with variables to trigger the mutation and optionally override options passed to `useMutation`. - `variables: TVariables` - Optional - The variables object to pass to the `mutationFn`. - Remaining options extend the same options described above in the `useMutation` hook. - - Lifecycle callbacks defined here will fire **after** those of the same type defined in the `useMutation`-level options. - `mutateAsync: (variables: TVariables, { onSuccess, onSettled, onError }) => Promise` - Similar to `mutate` but returns a promise which can be awaited. - `status: string` diff --git a/docs/src/pages/reference/useQueryClient.md b/docs/src/pages/reference/useQueryClient.md index 8099b1288c..edd12cbfc1 100644 --- a/docs/src/pages/reference/useQueryClient.md +++ b/docs/src/pages/reference/useQueryClient.md @@ -8,5 +8,5 @@ The `useQueryClient` hook returns the current `QueryClient` instance. ```js import { useQueryClient } from 'react-query' -const client = useQueryClient() +const queryClient = useQueryClient() ``` diff --git a/examples/auto-refetching/pages/index.js b/examples/auto-refetching/pages/index.js index 3f9f27336e..08c77d9f0e 100755 --- a/examples/auto-refetching/pages/index.js +++ b/examples/auto-refetching/pages/index.js @@ -7,25 +7,27 @@ import { useQuery, useQueryClient, useMutation, + MutationCache, QueryCache, QueryClient, QueryClientProvider, } from 'react-query' import { ReactQueryDevtools } from 'react-query-devtools' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const mutationCache = new MutationCache() +const queryClient = new QueryClient({ queryCache, mutationCache }) export default function App() { return ( - + ) } function Example() { - const client = useQueryClient() + const queryClient = useQueryClient() const [intervalMs, setIntervalMs] = React.useState(1000) const [value, setValue] = React.useState('') @@ -42,11 +44,11 @@ function Example() { ) const addMutation = useMutation(value => fetch(`/api/data?add=${value}`), { - onSuccess: () => client.invalidateQueries('todos'), + onSuccess: () => queryClient.invalidateQueries('todos'), }) const clearMutation = useMutation(() => fetch(`/api/data?clear=1`), { - onSuccess: () => client.invalidateQueries('todos'), + onSuccess: () => queryClient.invalidateQueries('todos'), }) if (status === 'loading') return

Loading...

diff --git a/examples/basic-graphql-request/src/index.js b/examples/basic-graphql-request/src/index.js index 42311d85cd..9449501011 100644 --- a/examples/basic-graphql-request/src/index.js +++ b/examples/basic-graphql-request/src/index.js @@ -13,14 +13,14 @@ import { request, gql } from "graphql-request"; const endpoint = "https://graphqlzero.almansi.me/api"; -const cache = new QueryCache(); -const client = new QueryClient({ cache }); +const queryCache = new QueryCache(); +const queryClient = new QueryClient({ queryCache }); function App() { const [postId, setPostId] = React.useState(-1); return ( - +

As you visit the posts below, you will notice them in a loading state the first time you load them. However, after you return to this list and @@ -63,7 +63,7 @@ function usePosts() { } function Posts({ setPostId }) { - const client = useQueryClient(); + const queryClient = useQueryClient(); const { status, data, error, isFetching } = usePosts(); return ( @@ -85,7 +85,7 @@ function Posts({ setPostId }) { style={ // We can use the queryCache here to show bold links for // ones that are cached - client.getQueryData(["post", post.id]) + queryClient.getQueryData(["post", post.id]) ? { fontWeight: "bold", color: "green", diff --git a/examples/basic/src/index.js b/examples/basic/src/index.js index a496d14268..6c8a6fcf4e 100644 --- a/examples/basic/src/index.js +++ b/examples/basic/src/index.js @@ -11,14 +11,14 @@ import { } from "react-query"; import { ReactQueryDevtools } from "react-query-devtools"; -const cache = new QueryCache(); -const client = new QueryClient({ cache }); +const queryCache = new QueryCache(); +const queryClient = new QueryClient({ queryCache }); function App() { const [postId, setPostId] = React.useState(-1); return ( - +

As you visit the posts below, you will notice them in a loading state the first time you load them. However, after you return to this list and @@ -49,7 +49,7 @@ function usePosts() { } function Posts({ setPostId }) { - const client = useQueryClient(); + const queryClient = useQueryClient(); const { status, data, error, isFetching } = usePosts(); return ( @@ -71,7 +71,7 @@ function Posts({ setPostId }) { style={ // We can use the queryCache here to show bold links for // ones that are cached - client.getQueryData(["post", post.id]) + queryClient.getQueryData(["post", post.id]) ? { fontWeight: "bold", color: "green", diff --git a/examples/custom-hooks/src/index.js b/examples/custom-hooks/src/index.js index 31ca53f99d..d1dd0204e2 100644 --- a/examples/custom-hooks/src/index.js +++ b/examples/custom-hooks/src/index.js @@ -12,14 +12,14 @@ import { ReactQueryDevtools } from "react-query-devtools"; import usePosts from "./hooks/usePosts"; import usePost from "./hooks/usePost"; -const cache = new QueryCache(); -const client = new QueryClient({ cache }); +const queryCache = new QueryCache(); +const queryClient = new QueryClient({ queryCache }); function App() { const [postId, setPostId] = React.useState(-1); return ( - +

This example is exactly the same as the basic example, but each query has been refactored to be it's own custom hook. This design is the @@ -37,7 +37,7 @@ function App() { } function Posts({ setPostId }) { - const client = useQueryClient(); + const queryClient = useQueryClient(); const { status, data, error, isFetching } = usePosts(); return ( @@ -59,7 +59,7 @@ function Posts({ setPostId }) { style={ // We can use the queryCache here to show bold links for // ones that are cached - client.getQueryData(["post", post.id]) + queryClient.getQueryData(["post", post.id]) ? { fontWeight: "bold", color: "green", diff --git a/examples/default-query-function/src/index.js b/examples/default-query-function/src/index.js index 8139a3935f..70b43d23b2 100644 --- a/examples/default-query-function/src/index.js +++ b/examples/default-query-function/src/index.js @@ -19,11 +19,11 @@ const defaultQueryFn = async (key) => { return data; }; -const cache = new QueryCache(); +const queryCache = new QueryCache(); // provide the default query function to your app via the query client -const client = new QueryClient({ - cache, +const queryClient = new QueryClient({ + queryCache, defaultOptions: { queries: { queryFn: defaultQueryFn, @@ -35,7 +35,7 @@ function App() { const [postId, setPostId] = React.useState(-1); return ( - +

As you visit the posts below, you will notice them in a loading state the first time you load them. However, after you return to this list and @@ -57,7 +57,7 @@ function App() { } function Posts({ setPostId }) { - const client = useQueryClient(); + const queryClient = useQueryClient(); // All you have to do now is pass a key! const { status, data, error, isFetching } = useQuery("/posts"); @@ -81,7 +81,7 @@ function Posts({ setPostId }) { style={ // We can use the queryCache here to show bold links for // ones that are cached - client.getQueryData(["post", post.id]) + queryClient.getQueryData(["post", post.id]) ? { fontWeight: "bold", color: "green", diff --git a/examples/focus-refetching/pages/index.js b/examples/focus-refetching/pages/index.js index 33b4bf209e..82f0e672ce 100755 --- a/examples/focus-refetching/pages/index.js +++ b/examples/focus-refetching/pages/index.js @@ -5,25 +5,27 @@ import { useQuery, useQueryClient, useMutation, + MutationCache, QueryCache, QueryClient, QueryClientProvider, } from 'react-query' import { ReactQueryDevtools } from 'react-query-devtools' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const mutationCache = new MutationCache() +const queryClient = new QueryClient({ queryCache, mutationCache }) export default function App() { return ( - + ) } function Example() { - const client = useQueryClient() + const queryClient = useQueryClient() const { status, data, error } = useQuery('user', async () => { const res = await axios.get('/api/user') @@ -31,11 +33,11 @@ function Example() { }) const logoutMutation = useMutation(logout, { - onSuccess: () => client.invalidateQueries('user'), + onSuccess: () => queryClient.invalidateQueries('user'), }) const loginMutation = useMutation(login, { - onSuccess: () => client.invalidateQueries('user'), + onSuccess: () => queryClient.invalidateQueries('user'), }) return ( diff --git a/examples/gql-blog/src/pages/Blog.js b/examples/gql-blog/src/pages/Blog.js index 77267a1ef3..f13f5d558c 100644 --- a/examples/gql-blog/src/pages/Blog.js +++ b/examples/gql-blog/src/pages/Blog.js @@ -1,21 +1,21 @@ -import React from "react"; -import { useQuery } from "react-query"; -import { Card, Box } from "sriracha-ui"; -import { client } from "../utils"; -import { gql } from "graphql-request"; -import { useParams } from "react-router"; -import ReactMarkdown from "react-markdown"; -import { PrismAsync as SyntaxHighlighter } from "react-syntax-highlighter"; -import dark from "../syntaxTheme"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from 'react' +import { useQuery } from 'react-query' +import { Card, Box } from 'sriracha-ui' +import { client } from '../utils' +import { gql } from 'graphql-request' +import { useParams } from 'react-router' +import ReactMarkdown from 'react-markdown' +import { PrismAsync as SyntaxHighlighter } from 'react-syntax-highlighter' +import dark from '../syntaxTheme' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' const CodeBlock = ({ language, value }) => { return ( {value} - ); -}; + ) +} const query = gql` query GetPubFileBySlug($slug: String!) { @@ -23,25 +23,25 @@ const query = gql` body } } -`; +` export default function Blog() { - const { slug } = useParams(); + const { slug } = useParams() - const { data, isLoading, status } = useQuery("posts", async () => { - const res = await client.request(query, { slug }); - return res; - }); + const { data, isLoading, status } = useQuery('posts', async () => { + const res = await client.request(query, { slug }) + return res + }) - const file = data?.getPubFileBySlug; - console.log("file:", file); + const file = data?.getPubFileBySlug + console.log('file:', file) - if (status === "error") + if (status === 'error') return ( The blog your looking for doesn't exist or was deleted by the user. - ); + ) return ( @@ -58,5 +58,5 @@ export default function Blog() { )} - ); + ) } diff --git a/examples/gql-blog/src/pages/Home.js b/examples/gql-blog/src/pages/Home.js index ffbaef8856..3808e1fffb 100644 --- a/examples/gql-blog/src/pages/Home.js +++ b/examples/gql-blog/src/pages/Home.js @@ -1,10 +1,9 @@ -import React from "react"; -import { Card, Text, Box, Flex, Img } from "sriracha-ui"; -import { Link } from "react-router-dom"; -import { gql } from "graphql-request"; -import { client } from "../utils"; -import { useQuery } from "react-query"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from 'react' +import { Card, Text, Box, Flex, Img } from 'sriracha-ui' +import { gql } from 'graphql-request' +import { client } from '../utils' +import { useQuery } from 'react-query' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' const query = gql` query GetPubFiles { @@ -17,29 +16,29 @@ const query = gql` body } } -`; +` export default function Home() { - const { data, isLoading } = useQuery("posts", async () => { - const res = await client.request(query); - return res; - }); - const files = data?.getPubFiles; + const { data, isLoading } = useQuery('posts', async () => { + const res = await client.request(query) + return res + }) + const files = data?.getPubFiles return ( Welcome To My Blog! - Powered by{" "} + Powered by{' '} Sriracha UI - ,{" "} + ,{' '} - Best Markdown Editor{" "} + Best Markdown Editor{' '} - and{" "} + and{' '} React Query @@ -51,7 +50,7 @@ export default function Home() { ) : ( <> - {files?.map((file) => ( + {files?.map(file => ( - ); + ) } diff --git a/examples/gql-blog/src/utils/index.js b/examples/gql-blog/src/utils/index.js index 8c7808ab55..c30ca1078f 100644 --- a/examples/gql-blog/src/utils/index.js +++ b/examples/gql-blog/src/utils/index.js @@ -1,10 +1,10 @@ -import { GraphQLClient } from "graphql-request"; +import { GraphQLClient } from 'graphql-request' -const endpoint = "https://best-markdown-editor-be.herokuapp.com/cms"; +const endpoint = 'https://best-markdown-editor-be.herokuapp.com/cms' export const client = new GraphQLClient(endpoint, { headers: { - "Content-Type": "application/json", - token: "588c454c-7176-457e-9d42-ad6d0e7ffdc4", + 'Content-Type': 'application/json', + token: '588c454c-7176-457e-9d42-ad6d0e7ffdc4', }, -}); +}) diff --git a/examples/load-more-infinite-scroll/pages/index.js b/examples/load-more-infinite-scroll/pages/index.js index aa3e92d798..d589c193e3 100755 --- a/examples/load-more-infinite-scroll/pages/index.js +++ b/examples/load-more-infinite-scroll/pages/index.js @@ -14,12 +14,12 @@ import { ReactQueryDevtools } from 'react-query-devtools' import useIntersectionObserver from '../hooks/useIntersectionObserver' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) export default function App() { return ( - + ) diff --git a/examples/optimistic-updates/pages/index.js b/examples/optimistic-updates/pages/index.js index b116c715fa..1e70bbb605 100755 --- a/examples/optimistic-updates/pages/index.js +++ b/examples/optimistic-updates/pages/index.js @@ -5,25 +5,27 @@ import { useQuery, useQueryClient, useMutation, + MutationCache, QueryCache, QueryClient, QueryClientProvider, } from 'react-query' import { ReactQueryDevtools } from 'react-query-devtools' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const mutationCache = new MutationCache() +const queryClient = new QueryClient({ queryCache, mutationCache }) export default function App() { return ( - + ) } function Example() { - const client = useQueryClient() + const queryClient = useQueryClient() const [text, setText] = React.useState('') const { status, data, error, isFetching } = useQuery('todos', async () => { const res = await axios.get('/api/data') @@ -38,11 +40,11 @@ function Example() { // an error onMutate: async text => { setText('') - await client.cancelQueries('todos') + await queryClient.cancelQueries('todos') - const previousValue = client.getQueryData('todos') + const previousValue = queryClient.getQueryData('todos') - client.setQueryData('todos', old => ({ + queryClient.setQueryData('todos', old => ({ ...old, items: [...old.items, text], })) @@ -51,10 +53,10 @@ function Example() { }, // On failure, roll back to the previous value onError: (err, variables, previousValue) => - client.setQueryData('todos', previousValue), + queryClient.setQueryData('todos', previousValue), // After success or failure, refetch the todos query onSettled: () => { - client.invalidateQueries('todos') + queryClient.invalidateQueries('todos') }, } ) diff --git a/examples/pagination/pages/index.js b/examples/pagination/pages/index.js index 1821d34f7e..72d927f6fe 100644 --- a/examples/pagination/pages/index.js +++ b/examples/pagination/pages/index.js @@ -9,19 +9,19 @@ import { } from 'react-query' import { ReactQueryDevtools } from 'react-query-devtools' -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) export default function App() { return ( - + ) } function Example() { - const client = useQueryClient() + const queryClient = useQueryClient() const [page, setPage] = React.useState(0) const fetchProjects = React.useCallback(async (key, page = 0) => { @@ -38,7 +38,7 @@ function Example() { // Prefetch the next page! React.useEffect(() => { if (data?.hasMore) { - client.prefetchQuery(['projects', page + 1], fetchProjects) + queryClient.prefetchQuery(['projects', page + 1], fetchProjects) } }, [data, fetchProjects, page]) diff --git a/examples/playground/src/index.js b/examples/playground/src/index.js index 03cece630f..01b833539b 100644 --- a/examples/playground/src/index.js +++ b/examples/playground/src/index.js @@ -4,10 +4,11 @@ import ReactDOM from "react-dom"; import { QueryClient, QueryClientProvider, + QueryCache, + MutationCache, useQuery, useQueryClient, useMutation, - QueryCache, } from "react-query"; import { ReactQueryDevtools } from "react-query-devtools"; @@ -28,8 +29,9 @@ let errorRate = 0.05; let queryTimeMin = 1000; let queryTimeMax = 2000; -const cache = new QueryCache(); -const client = new QueryClient({ cache }); +const queryCache = new QueryCache(); +const mutationCache = new MutationCache(); +const queryClient = new QueryClient({ queryCache, mutationCache }); function Root() { const [staleTime, setStaleTime] = React.useState(1000); @@ -49,7 +51,7 @@ function Root() { }, [localErrorRate, localFetchTimeMax, localFetchTimeMin]); React.useEffect(() => { - client.setDefaultOptions({ + queryClient.setDefaultOptions({ queries: { staleTime, cacheTime, @@ -58,7 +60,7 @@ function Root() { }, [cacheTime, staleTime]); return ( - +

The "staleTime" and "cacheTime" durations have been altered in this example to show how query stale-ness and query caching work on a @@ -130,7 +132,7 @@ function Root() { } function App() { - const client = useQueryClient(); + const queryClient = useQueryClient(); const [editingIndex, setEditingIndex] = React.useState(null); const [views, setViews] = React.useState(["", "fruit", "grape"]); // const [views, setViews] = React.useState([""]); @@ -138,7 +140,7 @@ function App() { return (

-
@@ -232,7 +234,7 @@ function Todos({ initialFilter = "", setEditingIndex }) { } function EditTodo({ editingIndex, setEditingIndex }) { - const client = useQueryClient(); + const queryClient = useQueryClient(); // Don't attempt to query until editingIndex is truthy const { status, data, isFetching, error, failureCount, refetch } = useQuery( @@ -256,8 +258,8 @@ function EditTodo({ editingIndex, setEditingIndex }) { const saveMutation = useMutation(patchTodo, { onSuccess: (data) => { // Update `todos` and the individual todo queries when this mutation succeeds - client.invalidateQueries("todos"); - client.setQueryData(["todo", { id: editingIndex }], data); + queryClient.invalidateQueries("todos"); + queryClient.setQueryData(["todo", { id: editingIndex }], data); }, }); @@ -337,12 +339,12 @@ function EditTodo({ editingIndex, setEditingIndex }) { } function AddTodo() { - const client = useQueryClient(); + const queryClient = useQueryClient(); const [name, setName] = React.useState(""); const addMutation = useMutation(postTodo, { onSuccess: () => { - client.invalidateQueries("todos"); + queryClient.invalidateQueries("todos"); }, }); diff --git a/examples/prefetching/pages/index.js b/examples/prefetching/pages/index.js index d0c19878d2..cd4fb35624 100755 --- a/examples/prefetching/pages/index.js +++ b/examples/prefetching/pages/index.js @@ -23,19 +23,19 @@ const getCharacter = async (key, selectedChar) => { return data } -const cache = new QueryCache() -const client = new QueryClient({ cache }) +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache }) export default function App() { return ( - + ) } function Example() { - const client = useQueryClient() + const queryClient = useQueryClient() const rerender = React.useReducer(d => d + 1)[1] const [selectedChar, setSelectedChar] = React.useState(1) @@ -48,10 +48,10 @@ function Example() { const prefetchNext = async id => { await Promise.all([ - client.prefetchQuery(['character', id + 1], getCharacter, { + queryClient.prefetchQuery(['character', id + 1], getCharacter, { staleTime: 5 * 60 * 1000, }), - client.prefetchQuery(['character', id - 1], getCharacter, { + queryClient.prefetchQuery(['character', id - 1], getCharacter, { staleTime: 5 * 60 * 1000, }), ]) @@ -79,7 +79,7 @@ function Example() { >
+ diff --git a/examples/simple/src/index.js b/examples/simple/src/index.js index e542a7783b..07c3818bca 100644 --- a/examples/simple/src/index.js +++ b/examples/simple/src/index.js @@ -9,12 +9,12 @@ import { } from "react-query"; import { ReactQueryDevtools } from "react-query-devtools"; -const cache = new QueryCache(); -const client = new QueryClient({ cache }); +const queryCache = new QueryCache(); +const queryClient = new QueryClient({ queryCache }); export default function App() { return ( - + ); diff --git a/examples/star-wars/src/App.js b/examples/star-wars/src/App.js index 5c430318b5..91b7766ede 100644 --- a/examples/star-wars/src/App.js +++ b/examples/star-wars/src/App.js @@ -9,12 +9,12 @@ import { ReactQueryDevtools } from "react-query-devtools"; import "./styles.css"; import Layout from "./Layout"; -const cache = new QueryCache(); -const client = new QueryClient({ cache }); +const queryCache = new QueryCache(); +const queryClient = new QueryClient({ queryCache }); export default function App() { return ( - + diff --git a/examples/suspense/src/components/Projects.js b/examples/suspense/src/components/Projects.js index b5053bc563..d2e3ade745 100644 --- a/examples/suspense/src/components/Projects.js +++ b/examples/suspense/src/components/Projects.js @@ -7,7 +7,7 @@ import Spinner from "./Spinner"; import { fetchProjects, fetchProject } from "../queries"; export default function Projects({ setActiveProject }) { - const client = useQueryClient(); + const queryClient = useQueryClient(); const { data, isFetching } = useQuery("projects", fetchProjects); return ( @@ -18,7 +18,7 @@ export default function Projects({ setActiveProject }) {