Skip to content

Commit

Permalink
refactor(core): use queryCache.get instead of find({ queryKey })
Browse files Browse the repository at this point in the history
…for better perf (#6918)

Methods that access a query by its `queryKey` can bypass `find` to avoid performance issues in large caches.

Co-authored-by: Sebastian Kranz <sebastian.kranz@nubix.de>
Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
  • Loading branch information
3 people committed Feb 18, 2024
1 parent 054eed5 commit 14aa69f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 10 deletions.
24 changes: 14 additions & 10 deletions packages/query-core/src/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ export class QueryClient {
: TQueryFnData,
>(queryKey: TTaggedQueryKey): TInferredQueryFnData | undefined
getQueryData(queryKey: QueryKey) {
return this.#queryCache.find({ queryKey })?.state.data
const options = this.defaultQueryOptions({ queryKey })
return this.#queryCache.get(options.queryHash)?.state.data
}

ensureQueryData<
Expand Down Expand Up @@ -165,14 +166,6 @@ export class QueryClient {
>,
options?: SetDataOptions,
): TInferredQueryFnData | undefined {
const query = this.#queryCache.find<TInferredQueryFnData>({ queryKey })
const prevData = query?.state.data
const data = functionalUpdate(updater, prevData)

if (typeof data === 'undefined') {
return undefined
}

const defaultedOptions = this.defaultQueryOptions<
any,
any,
Expand All @@ -181,6 +174,16 @@ export class QueryClient {
QueryKey
>({ queryKey })

const query = this.#queryCache.get<TInferredQueryFnData>(
defaultedOptions.queryHash,
)
const prevData = query?.state.data
const data = functionalUpdate(updater, prevData)

if (typeof data === 'undefined') {
return undefined
}

return this.#queryCache
.build(this, defaultedOptions)
.setData(data, { ...options, manual: true })
Expand All @@ -204,7 +207,8 @@ export class QueryClient {
getQueryState<TQueryFnData = unknown, TError = DefaultError>(
queryKey: QueryKey,
): QueryState<TQueryFnData, TError> | undefined {
return this.#queryCache.find<TQueryFnData, TError>({ queryKey })?.state
const options = this.defaultQueryOptions({ queryKey })
return this.#queryCache.get<TQueryFnData, TError>(options.queryHash)?.state
}

removeQueries(filters?: QueryFilters): void {
Expand Down
42 changes: 42 additions & 0 deletions packages/query-core/src/tests/queryClient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,16 @@ describe('queryClient', () => {
}),
)
})

test('should set 10k data in less than 500ms', () => {
const key = queryKey()
const start = performance.now()
for (let i = 0; i < 10000; i++) {
queryClient.setQueryData([key, i], i)
}
const end = performance.now()
expect(end - start).toBeLessThan(500)
})
})

describe('setQueriesData', () => {
Expand Down Expand Up @@ -419,6 +429,38 @@ describe('queryClient', () => {
queryClient.setQueryData([key, 'id'], 'bar')
expect(queryClient.getQueryData([key])).toBeUndefined()
})

test('should get 10k queries in less than 500ms', () => {
const key = queryKey()
for (let i = 0; i < 10000; i++) {
queryClient.setQueryData([key, i], i)
}

const start = performance.now()
for (let i = 0; i < 10000; i++) {
queryClient.getQueryData([key, i])
}
const end = performance.now()

expect(end - start).toBeLessThan(500)
})
})

describe('getQueryState', () => {
test('should get 10k queries in less than 500ms', () => {
const key = queryKey()
for (let i = 0; i < 10000; i++) {
queryClient.setQueryData([key, i], i)
}

const start = performance.now()
for (let i = 0; i < 10000; i++) {
queryClient.getQueryState([key, i])
}
const end = performance.now()

expect(end - start).toBeLessThan(500)
})
})

describe('ensureQueryData', () => {
Expand Down

0 comments on commit 14aa69f

Please sign in to comment.