diff --git a/.changeset/tiny-rabbits-tap.md b/.changeset/tiny-rabbits-tap.md new file mode 100644 index 0000000000..b6aa34d5ac --- /dev/null +++ b/.changeset/tiny-rabbits-tap.md @@ -0,0 +1,5 @@ +--- +'@tanstack/query-core': minor +--- + +Added queryHash to QueryFunctionContext, giving query functions direct access to the computed query hash. This provides first-class access to each query’s unique identifier and removes the need to manually import or recompute hashQueryKeyByOptions. diff --git a/docs/framework/react/guides/query-functions.md b/docs/framework/react/guides/query-functions.md index 7cd0aa7209..fb14aef312 100644 --- a/docs/framework/react/guides/query-functions.md +++ b/docs/framework/react/guides/query-functions.md @@ -100,6 +100,7 @@ function fetchTodoList({ queryKey }) { The `QueryFunctionContext` is the object passed to each query function. It consists of: - `queryKey: QueryKey`: [Query Keys](../query-keys.md) +- `queryHash: string`: The hash of the query key, used as a unique identifier for the query - `client: QueryClient`: [QueryClient](../../../../reference/QueryClient.md) - `signal?: AbortSignal` - [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) instance provided by TanStack Query diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts index 2f541788ab..db7d8396b5 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts +++ b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts @@ -343,6 +343,7 @@ describe('injectQuery', () => { client: queryClient, meta: undefined, queryKey: ['key8'], + queryHash: '["key8"]', signal: expect.anything(), }) }) diff --git a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx index db96ea17da..f606fb48b3 100644 --- a/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx +++ b/packages/query-core/src/__tests__/infiniteQueryBehavior.test.tsx @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { queryKey, sleep } from '@tanstack/query-test-utils' import { CancelledError, InfiniteQueryObserver, QueryClient } from '..' +import { hashQueryKeyByOptions } from '../utils' import type { InfiniteData, InfiniteQueryObserverResult, QueryCache } from '..' describe('InfiniteQueryBehavior', () => { @@ -82,6 +83,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 1, meta: undefined, @@ -96,6 +98,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 2, direction: 'forward', @@ -115,6 +118,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 0, direction: 'backward', @@ -135,6 +139,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: -1, meta: undefined, @@ -154,6 +159,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 1, meta: undefined, @@ -176,6 +182,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 0, meta: undefined, @@ -185,6 +192,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(2, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 1, meta: undefined, @@ -237,6 +245,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 1, meta: undefined, @@ -293,6 +302,7 @@ describe('InfiniteQueryBehavior', () => { expect(queryFnSpy).toHaveBeenNthCalledWith(1, { queryKey: key, + queryHash: hashQueryKeyByOptions(key), client: queryClient, pageParam: 2, meta: undefined, diff --git a/packages/query-core/src/__tests__/query.test.tsx b/packages/query-core/src/__tests__/query.test.tsx index f11bf173d3..524af851e4 100644 --- a/packages/query-core/src/__tests__/query.test.tsx +++ b/packages/query-core/src/__tests__/query.test.tsx @@ -254,6 +254,7 @@ describe('query', () => { expect(args).toBeDefined() expect(args.pageParam).toBeUndefined() expect(args.queryKey).toEqual(key) + expect(args.queryHash).toBe(hashQueryKeyByOptions(key)) expect(args.signal).toBeInstanceOf(AbortSignal) expect(args.client).toEqual(queryClient) }) diff --git a/packages/query-core/src/__tests__/queryClient.test-d.tsx b/packages/query-core/src/__tests__/queryClient.test-d.tsx index 8a3be1a9e2..57ffd7304d 100644 --- a/packages/query-core/src/__tests__/queryClient.test-d.tsx +++ b/packages/query-core/src/__tests__/queryClient.test-d.tsx @@ -203,6 +203,7 @@ describe('defaultOptions', () => { expectTypeOf(context).toEqualTypeOf<{ client: QueryClient queryKey: QueryKey + queryHash: string meta: Record | undefined signal: AbortSignal pageParam?: unknown diff --git a/packages/query-core/src/infiniteQueryBehavior.ts b/packages/query-core/src/infiniteQueryBehavior.ts index 476d90ce15..49daf0d8d9 100644 --- a/packages/query-core/src/infiniteQueryBehavior.ts +++ b/packages/query-core/src/infiniteQueryBehavior.ts @@ -61,6 +61,7 @@ export function infiniteQueryBehavior( > = { client: context.client, queryKey: context.queryKey, + queryHash: query.queryHash, pageParam: param, direction: previous ? 'backward' : 'forward', meta: context.options.meta, @@ -119,6 +120,7 @@ export function infiniteQueryBehavior( { client: context.client, queryKey: context.queryKey, + queryHash: query.queryHash, meta: context.options.meta, signal: context.signal, }, diff --git a/packages/query-core/src/query.ts b/packages/query-core/src/query.ts index a34c8630dc..f4208f4f5b 100644 --- a/packages/query-core/src/query.ts +++ b/packages/query-core/src/query.ts @@ -455,6 +455,7 @@ export class Query< > = { client: this.#client, queryKey: this.queryKey, + queryHash: this.queryHash, meta: this.meta, } addSignalProperty(queryFnContext) diff --git a/packages/query-core/src/types.ts b/packages/query-core/src/types.ts index ebfcf2c6bb..a28c50673b 100644 --- a/packages/query-core/src/types.ts +++ b/packages/query-core/src/types.ts @@ -142,6 +142,7 @@ export type QueryFunctionContext< ? { client: QueryClient queryKey: TQueryKey + queryHash: string signal: AbortSignal meta: QueryMeta | undefined pageParam?: unknown @@ -154,6 +155,7 @@ export type QueryFunctionContext< : { client: QueryClient queryKey: TQueryKey + queryHash: string signal: AbortSignal pageParam: TPageParam /** diff --git a/packages/query-persist-client-core/src/__tests__/createPersister.test.ts b/packages/query-persist-client-core/src/__tests__/createPersister.test.ts index d7b6550e63..51bf13eeac 100644 --- a/packages/query-persist-client-core/src/__tests__/createPersister.test.ts +++ b/packages/query-persist-client-core/src/__tests__/createPersister.test.ts @@ -30,14 +30,15 @@ function setupPersister( persisterOptions: StoragePersisterOptions, ) { const client = new QueryClient() + const queryHash = hashKey(queryKey) const context = { meta: { foo: 'bar' }, client, queryKey, + queryHash, // @ts-expect-error signal: undefined as AbortSignal, } satisfies QueryFunctionContext - const queryHash = hashKey(queryKey) const storageKey = `${PERSISTER_KEY_PREFIX}-${queryHash}` const queryFn = vi.fn()