Skip to content

Commit

Permalink
fix(core): do not inform QueriesObserver subscribers if combined resu…
Browse files Browse the repository at this point in the history
…lt hasn't changed (#8153)
  • Loading branch information
TkDodo authored Oct 9, 2024
1 parent 05ccd65 commit cae2524
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 7 deletions.
27 changes: 20 additions & 7 deletions packages/query-core/src/queriesObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class QueriesObserver<
#client: QueryClient
#result!: Array<QueryObserverResult>
#queries: Array<QueryObserverOptions>
#options?: QueriesObserverOptions<TCombinedResult>
#observers: Array<QueryObserver>
#combinedResult?: TCombinedResult
#lastCombine?: CombineFn<TCombinedResult>
Expand All @@ -46,11 +47,12 @@ export class QueriesObserver<
constructor(
client: QueryClient,
queries: Array<QueryObserverOptions<any, any, any, any, any>>,
_options?: QueriesObserverOptions<TCombinedResult>,
options?: QueriesObserverOptions<TCombinedResult>,
) {
super()

this.#client = client
this.#options = options
this.#queries = []
this.#observers = []
this.#result = []
Expand Down Expand Up @@ -83,10 +85,11 @@ export class QueriesObserver<

setQueries(
queries: Array<QueryObserverOptions>,
_options?: QueriesObserverOptions<TCombinedResult>,
options?: QueriesObserverOptions<TCombinedResult>,
notifyOptions?: NotifyOptions,
): void {
this.#queries = queries
this.#options = options

notifyManager.batch(() => {
const prevObservers = this.#observers
Expand Down Expand Up @@ -268,11 +271,21 @@ export class QueriesObserver<
}

#notify(): void {
notifyManager.batch(() => {
this.listeners.forEach((listener) => {
listener(this.#result)
})
})
if (this.hasListeners()) {
const previousResult = this.#combinedResult
const newResult = this.#combineResult(
this.#result,
this.#options?.combine,
)

if (previousResult !== newResult) {
notifyManager.batch(() => {
this.listeners.forEach((listener) => {
listener(this.#result)
})
})
}
}
}
}

Expand Down
117 changes: 117 additions & 0 deletions packages/react-query/src/__tests__/useQueries.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1431,4 +1431,121 @@ describe('useQueries', () => {
// state changed, re-run combine
expect(spy).toHaveBeenCalledTimes(4)
})

it('should not re-render if combine returns a stable reference', async () => {
const key1 = queryKey()
const key2 = queryKey()

const client = new QueryClient()

const queryFns: Array<string> = []
let renders = 0

function Page() {
const data = useQueries(
{
queries: [
{
queryKey: [key1],
queryFn: async () => {
await sleep(10)
queryFns.push('first result')
return 'first result'
},
},
{
queryKey: [key2],
queryFn: async () => {
await sleep(20)
queryFns.push('second result')
return 'second result'
},
},
],
combine: () => 'foo',
},
client,
)

renders++

return (
<div>
<div>data: {data}</div>
</div>
)
}

const rendered = render(<Page />)

await waitFor(() => rendered.getByText('data: foo'))

await waitFor(() =>
expect(queryFns).toEqual(['first result', 'second result']),
)

expect(renders).toBe(1)
})

it('should re-render once combine returns a different reference', async () => {
const key1 = queryKey()
const key2 = queryKey()
const key3 = queryKey()

const client = new QueryClient()

let renders = 0

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) => {
const isPending = results.some((res) => res.isPending)

return isPending ? 'pending' : 'foo'
},
},
client,
)

renders++

return (
<div>
<div>data: {data}</div>
</div>
)
}

const rendered = render(<Page />)

await waitFor(() => rendered.getByText('data: pending'))
await waitFor(() => rendered.getByText('data: foo'))

// one with pending, one with foo
expect(renders).toBe(2)
})
})

0 comments on commit cae2524

Please sign in to comment.