From 88f7da5e3fbe5211c351f4a77c3439168fcc4176 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Thu, 21 Oct 2021 17:17:21 -0300 Subject: [PATCH 01/13] feat(query): add meta field on query type --- src/core/types.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/types.ts b/src/core/types.ts index 8e461c5deb1..311f35fba97 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -44,6 +44,8 @@ export interface InfiniteData { pageParams: unknown[] } +export type QueryMeta = Record + export interface QueryOptions< TQueryFnData = unknown, TError = unknown, @@ -83,6 +85,11 @@ export interface QueryOptions< */ getNextPageParam?: GetNextPageParamFunction _defaulted?: boolean + /** + * Additional payload to be stored on each query. + * Use this property to pass information that can be used in other places. + */ + meta?: QueryMeta } export interface QueryObserverOptions< From 1cf655bb63d95204667cad9a3270377949f6db7c Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Thu, 21 Oct 2021 17:19:10 -0300 Subject: [PATCH 02/13] feat(query): store meta field as a property on class --- src/core/query.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/query.ts b/src/core/query.ts index 76e92dde31d..a9582291ae5 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -14,6 +14,7 @@ import type { QueryStatus, QueryFunctionContext, EnsuredQueryKey, + QueryMeta, } from './types' import type { QueryCache } from './queryCache' import type { QueryObserver } from './queryObserver' @@ -35,6 +36,7 @@ interface QueryConfig< options?: QueryOptions defaultOptions?: QueryOptions state?: QueryState + meta?: QueryMeta } export interface QueryState { @@ -152,6 +154,7 @@ export class Query< revertState?: QueryState state: QueryState cacheTime!: number + meta: QueryMeta private cache: QueryCache private promise?: Promise @@ -169,6 +172,7 @@ export class Query< this.queryHash = config.queryHash this.initialState = config.state || this.getDefaultState(this.options) this.state = this.initialState + this.meta = config.meta ?? {} this.scheduleGc() } From 626df0d8ccc9feadc2c96e2e1b37d3b8e798c919 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Thu, 21 Oct 2021 17:20:05 -0300 Subject: [PATCH 03/13] feat(query): update meta field on every configuration change --- src/core/query.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/query.ts b/src/core/query.ts index a9582291ae5..c825d674900 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -377,6 +377,10 @@ export class Query< this.setOptions(options) } + if (options?.meta) { + this.meta = options.meta + } + // Use the options from the first observer with a query function if no function is found. // This can happen when the query is hydrated or created with setQueryData. if (!this.options.queryFn) { From 8879644a9d5ca7a5139243271fc4bd2a90df8f39 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Thu, 21 Oct 2021 17:24:49 -0300 Subject: [PATCH 04/13] docs(useQuery): introduce meta field on options object --- docs/src/pages/reference/useQuery.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index aeffd9e7e01..c3703a472d3 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -33,6 +33,7 @@ const { initialDataUpdatedAt isDataEqual, keepPreviousData, + meta, notifyOnChangeProps, notifyOnChangePropsExclusions, onError, @@ -177,6 +178,9 @@ const result = useQuery({ - Set this to `true` if you want errors to be thrown in the render phase and propagate to the nearest error boundary - Set this to `false` to disable `suspense`'s default behaviour of throwing errors to the error boundary. - If set to a function, it will be passed the error and should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`) +- `meta: Record` + - Optional + - If set, stores additional information on the query cache entry that can be used as needed. It will be accessible wherever the `query` is available, and is also part of the `QueryFunctionContext` provided to the `queryFn`. **Returns** From 4d6cfd299de2ac252ff13cb7a2175c05ad002b9e Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Mon, 25 Oct 2021 14:22:01 -0300 Subject: [PATCH 05/13] chore(query): unconditionally set meta --- src/core/query.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/core/query.ts b/src/core/query.ts index c825d674900..4a7dcc0b605 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -181,6 +181,8 @@ export class Query< ): void { this.options = { ...this.defaultOptions, ...options } + this.meta = options?.meta + // Default to 5 minutes if not cache time is set this.cacheTime = Math.max( this.cacheTime || 0, @@ -377,10 +379,6 @@ export class Query< this.setOptions(options) } - if (options?.meta) { - this.meta = options.meta - } - // Use the options from the first observer with a query function if no function is found. // This can happen when the query is hydrated or created with setQueryData. if (!this.options.queryFn) { From 850a59b0b8e1241f58f1c553948bacdec03a8cd5 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Mon, 25 Oct 2021 14:23:36 -0300 Subject: [PATCH 06/13] feat(query): pass meta on queryFnContext --- src/core/query.ts | 1 + src/core/types.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/core/query.ts b/src/core/query.ts index 4a7dcc0b605..3e5c167fbdd 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -394,6 +394,7 @@ export class Query< const queryFnContext: QueryFunctionContext = { queryKey, pageParam: undefined, + meta: this.meta, } // Create fetch function diff --git a/src/core/types.ts b/src/core/types.ts index 311f35fba97..4a20e564977 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -19,6 +19,7 @@ export interface QueryFunctionContext< > { queryKey: EnsuredQueryKey pageParam?: TPageParam + meta?: QueryMeta } export type InitialDataFunction = () => T | undefined From 87021cbb763edef640e3615981dc1c42396556ac Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Mon, 25 Oct 2021 14:24:04 -0300 Subject: [PATCH 07/13] chore(query): do not default meta to an empty object --- src/core/query.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/query.ts b/src/core/query.ts index 3e5c167fbdd..185e4803f90 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -154,7 +154,7 @@ export class Query< revertState?: QueryState state: QueryState cacheTime!: number - meta: QueryMeta + meta?: QueryMeta private cache: QueryCache private promise?: Promise @@ -172,7 +172,7 @@ export class Query< this.queryHash = config.queryHash this.initialState = config.state || this.getDefaultState(this.options) this.state = this.initialState - this.meta = config.meta ?? {} + this.meta = config.meta this.scheduleGc() } From 0db0426f9cdeafc193a56ee6f176e34cba703d22 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Mon, 25 Oct 2021 14:59:45 -0300 Subject: [PATCH 08/13] chore(query): add meta option tests --- src/core/tests/query.test.tsx | 59 +++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index a9883c205c1..8801e8b58cb 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -472,4 +472,63 @@ describe('query', () => { unsubscribe1() expect(query?.getObserversCount()).toEqual(0) }) + + test('stores meta object in query', async () => { + const meta = { + it: 'works', + } + + const key = queryKey() + + await queryClient.prefetchQuery(key, () => 'data', { + meta, + }) + + const query = queryCache.find(key)! + + expect(query.meta).toBe(meta) + expect(query.options.meta).toBe(meta) + }) + + test('updates meta object on change', async () => { + const meta = { + it: 'works', + } + + const key = queryKey() + const queryFn = () => 'data' + + await queryClient.prefetchQuery(key, queryFn, { + meta, + }) + + await queryClient.prefetchQuery(key, queryFn, { + meta: undefined, + }) + + const query = queryCache.find(key)! + + expect(query.meta).toBeUndefined() + expect(query.options.meta).toBeUndefined() + }) + + test('provides meta object inside query function', async () => { + const meta = { + it: 'works', + } + + const queryFn = jest.fn(() => 'data') + + const key = queryKey() + + await queryClient.prefetchQuery(key, queryFn, { + meta, + }) + + expect(queryFn).toBeCalledWith( + expect.objectContaining({ + meta, + }) + ) + }) }) From b8317304d2b2cc1177a22b07ca456106e06b0112 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Tue, 26 Oct 2021 10:39:15 -0300 Subject: [PATCH 09/13] chore(query): explicitly set meta as Record | undefined instead of optional? --- src/core/query.ts | 4 ++-- src/core/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/query.ts b/src/core/query.ts index 185e4803f90..03d97bb490e 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -36,7 +36,7 @@ interface QueryConfig< options?: QueryOptions defaultOptions?: QueryOptions state?: QueryState - meta?: QueryMeta + meta: QueryMeta | undefined } export interface QueryState { @@ -154,7 +154,7 @@ export class Query< revertState?: QueryState state: QueryState cacheTime!: number - meta?: QueryMeta + meta: QueryMeta | undefined private cache: QueryCache private promise?: Promise diff --git a/src/core/types.ts b/src/core/types.ts index 4a20e564977..464dab1e79a 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -19,7 +19,7 @@ export interface QueryFunctionContext< > { queryKey: EnsuredQueryKey pageParam?: TPageParam - meta?: QueryMeta + meta: QueryMeta | undefined } export type InitialDataFunction = () => T | undefined From 38ecedc5f621ab49719ce29818f25de36bffa360 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Tue, 26 Oct 2021 10:42:17 -0300 Subject: [PATCH 10/13] feat(query): pass meta on fetch context --- src/core/query.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/query.ts b/src/core/query.ts index 03d97bb490e..4fde0c12c53 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -65,6 +65,7 @@ export interface FetchContext< options: QueryOptions queryKey: EnsuredQueryKey state: QueryState + meta: QueryMeta | undefined } export interface QueryBehavior< @@ -410,6 +411,7 @@ export class Query< queryKey: queryKey, state: this.state, fetchFn, + meta: this.meta, } if (this.options.behavior?.onFetch) { From 3dc056f265c3fcd1bdbd637288ae33e751f006fb Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Tue, 26 Oct 2021 10:46:52 -0300 Subject: [PATCH 11/13] feat(infiniteQuery): pass meta on query function context --- src/core/infiniteQueryBehavior.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/infiniteQueryBehavior.ts b/src/core/infiniteQueryBehavior.ts index b6a95380958..502621bcae5 100644 --- a/src/core/infiniteQueryBehavior.ts +++ b/src/core/infiniteQueryBehavior.ts @@ -60,6 +60,7 @@ export function infiniteQueryBehavior< const queryFnContext: QueryFunctionContext = { queryKey: context.queryKey, pageParam: param, + meta: context.meta, } const queryFnResult = queryFn(queryFnContext) From 3ffa0363ab1e6aefeaf88dc164f0bb2c3ae607e0 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Tue, 26 Oct 2021 11:00:16 -0300 Subject: [PATCH 12/13] feat(infiniteQuery): add tests related to the meta object --- src/core/tests/infiniteQueryObserver.test.tsx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/core/tests/infiniteQueryObserver.test.tsx b/src/core/tests/infiniteQueryObserver.test.tsx index d20834b8d8a..e0fff2a28b8 100644 --- a/src/core/tests/infiniteQueryObserver.test.tsx +++ b/src/core/tests/infiniteQueryObserver.test.tsx @@ -33,4 +33,32 @@ describe('InfiniteQueryObserver', () => { data: { pages: ['1'], pageParams: [undefined] }, }) }) + + test('InfiniteQueryObserver should pass the meta option to the queryFn', async () => { + const meta = { + it: 'works', + } + + const key = queryKey() + const queryFn = jest.fn(() => 1) + const observer = new InfiniteQueryObserver(queryClient, { + meta, + queryKey: key, + queryFn, + select: data => ({ + pages: data.pages.map(x => `${x}`), + pageParams: data.pageParams, + }), + }) + let observerResult + const unsubscribe = observer.subscribe(result => { + observerResult = result + }) + await sleep(1) + unsubscribe() + expect(observerResult).toMatchObject({ + data: { pages: ['1'], pageParams: [undefined] }, + }) + expect(queryFn).toBeCalledWith(expect.objectContaining({ meta })) + }) }) From 61c9177ef8f21da611af1711f275457333bc3227 Mon Sep 17 00:00:00 2001 From: Luis Felipe Zaguini Date: Tue, 26 Oct 2021 11:15:08 -0300 Subject: [PATCH 13/13] feat(queryCache): add meta field on query constructor --- src/core/queryCache.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/queryCache.ts b/src/core/queryCache.ts index 9f62f65c7a6..1a72d194be5 100644 --- a/src/core/queryCache.ts +++ b/src/core/queryCache.ts @@ -98,6 +98,7 @@ export class QueryCache extends Subscribable { options: client.defaultQueryOptions(options), state, defaultOptions: client.getQueryDefaults(queryKey), + meta: options.meta, }) this.add(query) }