From 0eeb0232354f19f434ef61a8b2aae492c008d562 Mon Sep 17 00:00:00 2001 From: Louis Law Date: Tue, 8 Feb 2022 11:15:57 -0800 Subject: [PATCH 1/5] Bail out if query data undefined --- docs/src/pages/reference/QueryClient.md | 4 ++ docs/src/pages/reference/useQuery.md | 4 +- src/core/query.ts | 62 +++++++++--------- src/core/queryClient.ts | 17 +++-- src/core/tests/query.test.tsx | 27 ++++++++ src/core/tests/queryClient.test.tsx | 86 ++++++++++++++++--------- src/core/tests/queryObserver.test.tsx | 4 +- src/core/types.ts | 6 +- src/reactjs/tests/ssr.test.tsx | 5 +- src/reactjs/tests/suspense.test.tsx | 29 +++++++-- src/reactjs/tests/useQuery.test.tsx | 4 +- 11 files changed, 171 insertions(+), 77 deletions(-) diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index 69e1448327..ff73335a0a 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -224,6 +224,8 @@ queryClient.setQueryData(queryKey, updater) setQueryData(queryKey, newData) ``` +If the value is `undefined`, the query data is not updated. + **Using an updater function** For convenience in syntax, you can also pass an updater function which receives the current data value and returns the new one: @@ -232,6 +234,8 @@ For convenience in syntax, you can also pass an updater function which receives setQueryData(queryKey, oldData => newData) ``` +If the updater function returns `undefined`, the query data will not be updated. + ## `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. diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index 4f628bebbc..e826bed046 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -68,7 +68,7 @@ const result = useQuery({ **Options** -- `queryKey: unknown[]` +- `queryKey: unknown[]` - **Required** - The query key to use for this query. - The query key will be hashed into a stable hash. See [Query Keys](../guides/query-keys) for more information. @@ -77,7 +77,7 @@ const result = useQuery({ - **Required, but only if no default query function has been defined** See [Default Query Function](../guides/default-query-function) for more information. - The function that the query will use to request data. - Receives a [QueryFunctionContext](../guides/query-functions#queryfunctioncontext) - - Must return a promise that will either resolve data or throw an error. + - Must return a promise that will either resolve data or throw an error. The data cannot be `undefined`. - `enabled: boolean` - Set this to `false` to disable this query from automatically running. - Can be used for [Dependent Queries](../guides/dependent-queries). diff --git a/src/core/query.ts b/src/core/query.ts index 06935088dc..be087dbbea 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -1,7 +1,5 @@ import { getAbortController, - Updater, - functionalUpdate, noop, replaceEqualDeep, timeUntilStale, @@ -195,14 +193,11 @@ export class Query< } setData( - updater: Updater, + data: TData, options?: SetDataOptions & { notifySuccess: boolean } ): TData { const prevData = this.state.data - // Get the new data - let data = functionalUpdate(updater, prevData) - // Use prev data if an isDataEqual function is defined and returns `true` if (this.options.isDataEqual?.(prevData, data)) { data = prevData as TData @@ -438,11 +433,41 @@ export class Query< this.dispatch({ type: 'fetch', meta: context.fetchOptions?.meta }) } + const onError = (error: TError | { silent?: boolean }) => { + // Optimistically update state if needed + if (!(isCancelledError(error) && error.silent)) { + this.dispatch({ + type: 'error', + error: error as TError, + }) + } + + if (!isCancelledError(error)) { + // Notify cache callback + this.cache.config.onError?.(error, this as Query) + + if (process.env.NODE_ENV !== 'production') { + getLogger().error(error) + } + } + + if (!this.isFetchingOptimistic) { + // Schedule query gc after fetching + this.scheduleGc() + } + this.isFetchingOptimistic = false + } + // Try to fetch the data this.retryer = createRetryer({ fn: context.fetchFn as () => TData, abort: abortController?.abort?.bind(abortController), onSuccess: data => { + if (typeof data === 'undefined') { + onError(new Error('Query data cannot be undefined') as any) + return + } + this.setData(data as TData) // Notify cache callback @@ -454,30 +479,7 @@ export class Query< } this.isFetchingOptimistic = false }, - onError: (error: TError | { silent?: boolean }) => { - // Optimistically update state if needed - if (!(isCancelledError(error) && error.silent)) { - this.dispatch({ - type: 'error', - error: error as TError, - }) - } - - if (!isCancelledError(error)) { - // Notify cache callback - this.cache.config.onError?.(error, this as Query) - - if (process.env.NODE_ENV !== 'production') { - getLogger().error(error) - } - } - - if (!this.isFetchingOptimistic) { - // Schedule query gc after fetching - this.scheduleGc() - } - this.isFetchingOptimistic = false - }, + onError, onFail: () => { this.dispatch({ type: 'failed' }) }, diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index d09461ef83..589b723504 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -8,6 +8,7 @@ import { partialMatchKey, hashQueryKeyByOptions, MutationFilters, + functionalUpdate, } from './utils' import type { QueryClientConfig, @@ -125,14 +126,22 @@ export class QueryClient { setQueryData( queryKey: QueryKey, - updater: Updater, + updater: Updater | undefined, options?: SetDataOptions - ): TData { + ): TData | undefined { + const query = this.queryCache.find(queryKey) + const prevData = query?.state.data + const data = functionalUpdate(updater, prevData) + + if (typeof data === 'undefined') { + return undefined + } + const parsedOptions = parseQueryArgs(queryKey) const defaultedOptions = this.defaultQueryOptions(parsedOptions) return this.queryCache .build(this, defaultedOptions) - .setData(updater, { ...options, notifySuccess: false }) + .setData(data, { ...options, notifySuccess: false }) } setQueriesData( @@ -151,7 +160,7 @@ export class QueryClient { queryKeyOrFilters: QueryKey | QueryFilters, updater: Updater, options?: SetDataOptions - ): [QueryKey, TData][] { + ): [QueryKey, TData | undefined][] { return notifyManager.batch(() => this.getQueryCache() .findAll(queryKeyOrFilters) diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index cc120b933d..b5aefa4b14 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -12,6 +12,7 @@ import { isError, onlineManager, QueryFunctionContext, + QueryObserverResult, } from '../..' import { waitFor } from '@testing-library/react' @@ -787,6 +788,7 @@ describe('query', () => { let signalTest: any await queryClient.prefetchQuery(key, ({ signal }) => { signalTest = signal + return 'data' }) expect(signalTest).toBeUndefined() @@ -814,6 +816,31 @@ describe('query', () => { consoleMock.mockRestore() }) + test('fetch should dispatch an error if the queryFn returns undefined', async () => { + const key = queryKey() + + const observer = new QueryObserver(queryClient, { + queryKey: key, + queryFn: (() => undefined) as any, + retry: false, + }) + + let observerResult: QueryObserverResult | undefined + + const unsubscribe = observer.subscribe(result => { + observerResult = result + }) + + await sleep(10) + + expect(observerResult).toMatchObject({ + isError: true, + error: new Error('Query data cannot be undefined'), + }) + + unsubscribe() + }) + test('fetch should dispatch fetch if is fetching and current promise is undefined', async () => { const key = queryKey() diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index c32cf97cc0..0d60ff4206 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -53,7 +53,7 @@ describe('queryClient', () => { }, }) - const fetchData = () => Promise.resolve(undefined) + const fetchData = () => Promise.resolve('data') await testClient.prefetchQuery(key, fetchData) const newQuery = testClient.getQueryCache().find(key) expect(newQuery?.options.cacheTime).toBe(Infinity) @@ -301,6 +301,34 @@ describe('queryClient', () => { expect(queryClient.getQueryData(key)).toBe('qux') }) + test('should not create a new query if query was not found and data is undefined', () => { + const key = queryKey() + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + queryClient.setQueryData(key, undefined) + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + }) + + test('should not create a new query if query was not found and updater returns undefined', () => { + const key = queryKey() + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + queryClient.setQueryData(key, () => undefined) + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + }) + + test('should not update query data if data is undefined', () => { + const key = queryKey() + queryClient.setQueryData(key, 'qux') + queryClient.setQueryData(key, undefined) + expect(queryClient.getQueryData(key)).toBe('qux') + }) + + test('should not update query data if updater returns undefined', () => { + const key = queryKey() + queryClient.setQueryData(key, 'qux') + queryClient.setQueryData(key, () => undefined) + expect(queryClient.getQueryData(key)).toBe('qux') + }) + test('should accept an update function', () => { const key = queryKey() @@ -871,8 +899,8 @@ describe('queryClient', () => { test('should refetch all queries when no arguments are given', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer1 = new QueryObserver(queryClient, { @@ -897,8 +925,8 @@ describe('queryClient', () => { test('should be able to refetch all fresh queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -916,8 +944,8 @@ describe('queryClient', () => { test('should be able to refetch all stale queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -936,8 +964,8 @@ describe('queryClient', () => { test('should be able to refetch all stale and active queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) queryClient.invalidateQueries(key1) @@ -958,8 +986,8 @@ describe('queryClient', () => { test('should be able to refetch all active and inactive queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -977,8 +1005,8 @@ describe('queryClient', () => { test('should be able to refetch all active and inactive queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -996,8 +1024,8 @@ describe('queryClient', () => { test('should be able to refetch only active queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1015,8 +1043,8 @@ describe('queryClient', () => { test('should be able to refetch only inactive queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1060,8 +1088,8 @@ describe('queryClient', () => { test('should refetch active queries by default', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1079,8 +1107,8 @@ describe('queryClient', () => { test('should not refetch inactive queries by default', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1098,8 +1126,8 @@ describe('queryClient', () => { test('should not refetch active queries when "refetch" is "none"', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1119,8 +1147,8 @@ describe('queryClient', () => { test('should refetch inactive queries when "refetch" is "inactive"', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1142,8 +1170,8 @@ describe('queryClient', () => { test('should refetch active and inactive queries when "refetch" is "all"', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1269,8 +1297,8 @@ describe('queryClient', () => { test('should refetch all active queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') const observer1 = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index 9daf7d8207..ed95023aaf 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -331,7 +331,7 @@ describe('queryObserver', () => { test('should be able to watch a query without defining a query function', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const callback = jest.fn() const observer = new QueryObserver(queryClient, { queryKey: key, @@ -346,7 +346,7 @@ describe('queryObserver', () => { test('should accept unresolved query config in update function', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const observer = new QueryObserver(queryClient, { queryKey: key, enabled: false, diff --git a/src/core/types.ts b/src/core/types.ts index f177030963..9df4e6a954 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -6,10 +6,14 @@ import type { QueryCache } from './queryCache' import type { MutationCache } from './mutationCache' export type QueryKey = readonly unknown[] +export type QueryFunctionData = T extends undefined ? never : T + export type QueryFunction< T = unknown, TQueryKey extends QueryKey = QueryKey -> = (context: QueryFunctionContext) => T | Promise +> = ( + context: QueryFunctionContext +) => QueryFunctionData | Promise> export interface QueryFunctionContext< TQueryKey extends QueryKey = QueryKey, diff --git a/src/reactjs/tests/ssr.test.tsx b/src/reactjs/tests/ssr.test.tsx index 2363a160c0..1a2c738331 100644 --- a/src/reactjs/tests/ssr.test.tsx +++ b/src/reactjs/tests/ssr.test.tsx @@ -60,7 +60,10 @@ describe('Server Side Rendering', () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) const key = queryKey() - const queryFn = jest.fn(() => sleep(10)) + const queryFn = jest.fn(() => { + sleep(10) + return 'data' + }) function Page() { const query = useQuery(key, queryFn) diff --git a/src/reactjs/tests/suspense.test.tsx b/src/reactjs/tests/suspense.test.tsx index a4b68d4210..6bf53b56ea 100644 --- a/src/reactjs/tests/suspense.test.tsx +++ b/src/reactjs/tests/suspense.test.tsx @@ -114,7 +114,10 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() const queryFn = jest.fn() - queryFn.mockImplementation(() => sleep(10)) + queryFn.mockImplementation(() => { + sleep(10) + return 'data' + }) function Page() { useQuery([key], queryFn, { suspense: true }) @@ -138,7 +141,14 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery(key, () => sleep(10), { suspense: true }) + useQuery( + key, + () => { + sleep(10) + return 'data' + }, + { suspense: true } + ) return <>rendered } @@ -212,10 +222,17 @@ describe("useQuery's in Suspense mode", () => { const successFn2 = jest.fn() function FirstComponent() { - useQuery(key, () => sleep(10), { - suspense: true, - onSuccess: successFn1, - }) + useQuery( + key, + () => { + sleep(10) + return 'data' + }, + { + suspense: true, + onSuccess: successFn1, + } + ) return first } diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index 93e1ea3e52..6a68bd126d 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -3447,7 +3447,7 @@ describe('useQuery', () => { const [enabled, setEnabled] = React.useState(false) const [isPrefetched, setPrefetched] = React.useState(false) - const query = useQuery(key, () => undefined, { + const query = useQuery(key, () => 'data', { enabled, }) @@ -3609,7 +3609,7 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const query = useQuery(key, () => undefined, { + const query = useQuery(key, () => 'data', { enabled: false, }) From 19bbad182229a16c261e56fc0d558eda2d64e2f1 Mon Sep 17 00:00:00 2001 From: phatmann Date: Tue, 8 Feb 2022 12:54:42 -0800 Subject: [PATCH 2/5] Fix failing test --- src/reactjs/tests/useQueries.test.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index 1ca4f48c3b..4a0bb9d286 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -933,10 +933,12 @@ describe('useQueries', () => { return { queryKey: key, onError: err, - queryFn: (ctx: QueryFunctionContext) => { - expectType(ctx.queryKey) - return fn?.call({}, ctx) - }, + queryFn: fn + ? (ctx: QueryFunctionContext) => { + expectType(ctx.queryKey) + return fn.call({}, ctx) + } + : undefined, } } ), From 9e60a98823c94fb63fd565b553d3fb32cc633ac5 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 9 Feb 2022 19:54:16 +0100 Subject: [PATCH 3/5] docs: migration guide for undefined data --- .../guides/migrating-to-react-query-4.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 4856dbbeb6..c3cdca63a0 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -245,6 +245,21 @@ Types now require using TypeScript v4.1 or greater Starting with v4, react-query will no longer log errors (e.g. failed fetches) to the console in production mode, as this was confusing to many. Errors will still show up in development mode. +### Undefined is an illegale cache value for successful queries + +In order to make bailing out of updates possible by returning `undefined`, we had to make `undefined` an illegal cache value. This is in-line with other concepts of react-query, for example, returning `undefined` from the [initialData function](guides/initial-query-data#initial-data-function) will also _not_ set data. + +Further, it is an easy bug to produce `Promise` by adding logging in the queryFn: + +```js +useQuery( + ['key'], + () => axios.get(url).then(result => console.log(result.data) +) +``` + +This is now disallowed on type level; at runtime, `undefined` will be transformed to a _failed Promise_, which means you will get an `error`, which will also be logged to the console in development mode. + ## New Features 🚀 ### Proper offline support @@ -265,3 +280,14 @@ Mutations can now also be garbage collected automatically, just like queries. Th ### Tracked Queries per default React Query defaults to "tracking" query properties, which should give you a nice boost in render optimization. The feature has existed since [v3.6.0](https://github.com/tannerlinsley/react-query/releases/tag/v3.6.0) and has now become the default behavior with v4. + +### Bailing out of updates with setQueryData + +When using the [functional updater form of setQueryData](../reference/QueryClient#queryclientsetquerydata), you can now bail out of the update by returning `undefined`. This is helpful if `undefined` is given to you as `previousValue`, which means that currently, no cached entry exists and you don't want to / cannot create one, like in the example of toggling a todo: + +```js + queryClient.setQueryData( + ['todo', id], + (previousTodo) => previousTodo ? { ...previousTodo, done: true } : undefined +) + ``` From fec2881fcf7c5cdad395598532a81d61e92e6ec3 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 9 Feb 2022 19:58:52 +0100 Subject: [PATCH 4/5] docs: update setQueryData reference --- docs/src/pages/reference/QueryClient.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index ff73335a0a..2c4ab70462 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -214,7 +214,7 @@ queryClient.setQueryData(queryKey, updater) **Options** - `queryKey: QueryKey`: [Query Keys](../guides/query-keys) -- `updater: TData | (oldData: TData | undefined) => TData` +- `updater: TData | (oldData: TData | undefined) => TData | undefined` - If non-function is passed, the data will be updated to this value - If a function is passed, it will receive the old data value and be expected to return a new one. @@ -234,7 +234,7 @@ For convenience in syntax, you can also pass an updater function which receives setQueryData(queryKey, oldData => newData) ``` -If the updater function returns `undefined`, the query data will not be updated. +If the updater function returns `undefined`, the query data will not be updated. If the updater function receives `undefined` as input, you can return `undefined` to bail out of the update and thus _not_ create a new cache entry. ## `queryClient.getQueryState` From 5a666baa14db4a74a1aa2e6448b54da939041875 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 9 Feb 2022 20:02:35 +0100 Subject: [PATCH 5/5] Update docs/src/pages/guides/migrating-to-react-query-4.md --- docs/src/pages/guides/migrating-to-react-query-4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index c3cdca63a0..6cb9773cf7 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -254,7 +254,7 @@ Further, it is an easy bug to produce `Promise` by adding logging in the q ```js useQuery( ['key'], - () => axios.get(url).then(result => console.log(result.data) + () => axios.get(url).then(result => console.log(result.data)) ) ```