Skip to content

Commit

Permalink
fix(useQueries): make sure we don't lose property tracking (#8295)
Browse files Browse the repository at this point in the history
if we call `combine` with `#this.result`, we don't have a tracked version of it, as tracking happens in useQueries (to avoid tracking internal access)
  • Loading branch information
TkDodo authored Nov 15, 2024
1 parent 0df2883 commit 4a2e838
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 13 deletions.
33 changes: 21 additions & 12 deletions packages/query-core/src/queriesObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,30 @@ export class QueriesObserver<
return this.#combineResult(r ?? result, combine)
},
() => {
return matches.map((match, index) => {
const observerResult = result[index]!
return !match.defaultedQueryOptions.notifyOnChangeProps
? match.observer.trackResult(observerResult, (accessedProp) => {
// track property on all observers to ensure proper (synchronized) tracking (#7000)
matches.forEach((m) => {
m.observer.trackProp(accessedProp)
})
})
: observerResult
})
return this.#trackResult(result, queries)
},
]
}

#trackResult(
result: Array<QueryObserverResult>,
queries: Array<QueryObserverOptions>,
) {
const matches = this.#findMatchingObservers(queries)

return matches.map((match, index) => {
const observerResult = result[index]!
return !match.defaultedQueryOptions.notifyOnChangeProps
? match.observer.trackResult(observerResult, (accessedProp) => {
// track property on all observers to ensure proper (synchronized) tracking (#7000)
matches.forEach((m) => {
m.observer.trackProp(accessedProp)
})
})
: observerResult
})
}

#combineResult(
input: Array<QueryObserverResult>,
combine: CombineFn<TCombinedResult> | undefined,
Expand Down Expand Up @@ -267,7 +276,7 @@ export class QueriesObserver<
if (this.hasListeners()) {
const previousResult = this.#combinedResult
const newResult = this.#combineResult(
this.#result,
this.#trackResult(this.#result, this.#queries),
this.#options?.combine,
)

Expand Down
73 changes: 73 additions & 0 deletions packages/react-query/src/__tests__/useQueries.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1548,4 +1548,77 @@ describe('useQueries', () => {
// one with pending, one with foo
expect(renders).toBe(2)
})

it('should track properties correctly with combine', async () => {
const key1 = queryKey()
const key2 = queryKey()
const key3 = queryKey()

const client = new QueryClient()

function Page() {
const data = useQueries(
{
queries: [
{
queryKey: [key1],
queryFn: async () => {
await sleep(10)
return 'first result'
},
},
{
queryKey: [key2],
queryFn: async () => {
await sleep(15)
return 'second result'
},
},
{
queryKey: [key3],
queryFn: async () => {
await sleep(20)
return 'third result'
},
},
],
combine: (results) => {
if (results.find((r) => r.isPending)) {
return 'pending'
}
return results.map((r) => r.data).join(', ')
},
},
client,
)

return (
<div>
<div>data: {data}</div>
<button
onClick={() => {
client.setQueryData([key1], 'first result updated')
}}
>
update
</button>
</div>
)
}

const rendered = render(<Page />)

await waitFor(() => rendered.getByText('data: pending'))
await waitFor(() =>
rendered.getByText('data: first result, second result, third result'),
)

fireEvent.click(rendered.getByRole('button', { name: /update/i }))

await waitFor(() =>
rendered.getByText(
'data: first result updated, second result, third result',
),
)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ describe('useSuspenseQueries 2', () => {
queryKey: key,
queryFn: async () => {
count++
console.log('queryFn')
throw new Error('Query failed')
},
gcTime: 0,
Expand Down

0 comments on commit 4a2e838

Please sign in to comment.