diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts index cf346bfc3a..b3535f1cc2 100644 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ b/packages/angular-query-experimental/src/inject-queries.ts @@ -236,8 +236,10 @@ export function injectQueries< ) }) - const [, getCombinedResult] = - observer.getOptimisticResult(defaultedQueries()) + const [, getCombinedResult] = observer.getOptimisticResult( + defaultedQueries(), + (options as QueriesObserverOptions).combine, + ) const result = signal(getCombinedResult() as any) diff --git a/packages/query-core/src/queriesObserver.ts b/packages/query-core/src/queriesObserver.ts index 124b06f265..e017a5a08a 100644 --- a/packages/query-core/src/queriesObserver.ts +++ b/packages/query-core/src/queriesObserver.ts @@ -22,10 +22,14 @@ function replaceAt(array: Array, index: number, value: T): Array { type QueriesObserverListener = (result: Array) => void +type CombineFn = ( + result: Array, +) => TCombinedResult + export interface QueriesObserverOptions< TCombinedResult = Array, > { - combine?: (result: Array) => TCombinedResult + combine?: CombineFn } export class QueriesObserver< @@ -55,7 +59,7 @@ export class QueriesObserver< #setResult(value: Array) { this.#result = value - this.#combinedResult = this.#combineResult(value) + this.#combinedResult = this.#combineResult(value, this.#options?.combine) } protected onSubscribe(): void { @@ -151,6 +155,7 @@ export class QueriesObserver< getOptimisticResult( queries: Array, + combine: CombineFn | undefined, ): [ rawResult: Array, combineResult: (r?: Array) => TCombinedResult, @@ -164,7 +169,7 @@ export class QueriesObserver< return [ result, (r?: Array) => { - return this.#combineResult(r ?? result) + return this.#combineResult(r ?? result, combine) }, () => { return matches.map((match, index) => { @@ -177,8 +182,10 @@ export class QueriesObserver< ] } - #combineResult(input: Array): TCombinedResult { - const combine = this.#options?.combine + #combineResult( + input: Array, + combine: CombineFn | undefined, + ): TCombinedResult { if (combine) { return replaceEqualDeep(this.#combinedResult, combine(input)) } diff --git a/packages/react-query/src/__tests__/useQueries.test.tsx b/packages/react-query/src/__tests__/useQueries.test.tsx index 19a2badf6f..873c2c6bfc 100644 --- a/packages/react-query/src/__tests__/useQueries.test.tsx +++ b/packages/react-query/src/__tests__/useQueries.test.tsx @@ -1167,4 +1167,46 @@ describe('useQueries', () => { // no further re-render because data didn't change expect(results.length).toBe(length) }) + + it('should not have stale closures with combine (#6648)', async () => { + const key = queryKey() + + function Page() { + const [count, setCount] = React.useState(0) + const queries = useQueries( + { + queries: [ + { + queryKey: key, + queryFn: () => Promise.resolve('result'), + }, + ], + combine: (results) => { + return { + count, + res: results.map((res) => res.data).join(','), + } + }, + }, + queryClient, + ) + + return ( +
+
+ data: {String(queries.count)} {queries.res} +
+ +
+ ) + } + + const rendered = render() + + await waitFor(() => rendered.getByText('data: 0 result')) + + fireEvent.click(rendered.getByRole('button', { name: /inc/i })) + + await waitFor(() => rendered.getByText('data: 1 result')) + }) }) diff --git a/packages/react-query/src/useQueries.ts b/packages/react-query/src/useQueries.ts index b2c000d922..9cad13b71c 100644 --- a/packages/react-query/src/useQueries.ts +++ b/packages/react-query/src/useQueries.ts @@ -282,7 +282,10 @@ export function useQueries< ) const [optimisticResult, getCombinedResult, trackResult] = - observer.getOptimisticResult(defaultedQueries) + observer.getOptimisticResult( + defaultedQueries, + (options as QueriesObserverOptions).combine, + ) React.useSyncExternalStore( React.useCallback( diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts index 36248692a2..5fad2698f0 100644 --- a/packages/solid-query/src/createQueries.ts +++ b/packages/solid-query/src/createQueries.ts @@ -239,13 +239,23 @@ export function createQueries< ) const [state, setState] = createStore( - observer.getOptimisticResult(defaultedQueries())[1](), + observer.getOptimisticResult( + defaultedQueries(), + (queriesOptions() as QueriesObserverOptions).combine, + )[1](), ) createRenderEffect( on( () => queriesOptions().queries.length, - () => setState(observer.getOptimisticResult(defaultedQueries())[1]()), + () => + setState( + observer.getOptimisticResult( + defaultedQueries(), + (queriesOptions() as QueriesObserverOptions) + .combine, + )[1](), + ), ), ) diff --git a/packages/svelte-query/src/createQueries.ts b/packages/svelte-query/src/createQueries.ts index d5e8a75e81..4eb497d9a8 100644 --- a/packages/svelte-query/src/createQueries.ts +++ b/packages/svelte-query/src/createQueries.ts @@ -256,7 +256,10 @@ export function createQueries< // @ts-ignore svelte-check thinks this is unused ([$result, $defaultedQueriesStore]) => { const [rawResult, combineResult, trackResult] = - observer.getOptimisticResult($defaultedQueriesStore) + observer.getOptimisticResult( + $defaultedQueriesStore, + (options as QueriesObserverOptions).combine, + ) $result = rawResult return combineResult(trackResult()) }, diff --git a/packages/vue-query/src/useQueries.ts b/packages/vue-query/src/useQueries.ts index d6bf5a7980..3b190b374d 100644 --- a/packages/vue-query/src/useQueries.ts +++ b/packages/vue-query/src/useQueries.ts @@ -292,6 +292,7 @@ export function useQueries< ) const [, getCombinedResult] = observer.getOptimisticResult( defaultedQueries.value, + (options as QueriesObserverOptions).combine, ) const state = ref(getCombinedResult()) as Ref @@ -307,12 +308,14 @@ export function useQueries< unsubscribe = observer.subscribe(() => { const [, getCombinedResultRestoring] = observer.getOptimisticResult( defaultedQueries.value, + (options as QueriesObserverOptions).combine, ) state.value = getCombinedResultRestoring() }) // Subscription would not fire for persisted results const [, getCombinedResultPersisted] = observer.getOptimisticResult( defaultedQueries.value, + (options as QueriesObserverOptions).combine, ) state.value = getCombinedResultPersisted() } @@ -329,6 +332,7 @@ export function useQueries< ) const [, getCombinedResultPersisted] = observer.getOptimisticResult( defaultedQueries.value, + (options as QueriesObserverOptions).combine, ) state.value = getCombinedResultPersisted() },