Skip to content

Commit 6bbb371

Browse files
authored
fix(useQueries): make sure keepPreviousData is respected (TanStack#2340)
1 parent 418c48f commit 6bbb371

File tree

2 files changed

+207
-10
lines changed

2 files changed

+207
-10
lines changed

src/core/queriesObserver.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,24 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
6464
}
6565

6666
getOptimisticResult(queries: QueryObserverOptions[]): QueryObserverResult[] {
67-
return queries.map(options => {
67+
return queries.map((options, index) => {
6868
const defaultedOptions = this.client.defaultQueryObserverOptions(options)
69-
return this.getObserver(defaultedOptions).getOptimisticResult(
69+
return this.getObserver(defaultedOptions, index).getOptimisticResult(
7070
defaultedOptions
7171
)
7272
})
7373
}
7474

75-
private getObserver(options: QueryObserverOptions): QueryObserver {
75+
private getObserver(
76+
options: QueryObserverOptions,
77+
index: number
78+
): QueryObserver {
7679
const defaultedOptions = this.client.defaultQueryObserverOptions(options)
77-
return (
78-
this.observersMap[defaultedOptions.queryHash!] ||
79-
new QueryObserver(this.client, defaultedOptions)
80-
)
80+
let currentObserver = this.observersMap[defaultedOptions.queryHash!]
81+
if (!currentObserver && defaultedOptions.keepPreviousData) {
82+
currentObserver = this.observers[index]
83+
}
84+
return currentObserver ?? new QueryObserver(this.client, defaultedOptions)
8185
}
8286

8387
private updateObservers(notifyOptions?: NotifyOptions): void {
@@ -96,9 +100,9 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
96100
options
97101
)
98102
const queryHash = defaultedOptions.queryHash!
99-
const observer = this.getObserver(defaultedOptions)
103+
const observer = this.getObserver(defaultedOptions, i)
100104

101-
if (prevObserversMap[queryHash]) {
105+
if (prevObserversMap[queryHash] || defaultedOptions.keepPreviousData) {
102106
observer.setOptions(defaultedOptions, notifyOptions)
103107
}
104108

src/react/tests/useQueries.test.tsx

Lines changed: 194 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { waitFor } from '@testing-library/react'
12
import React from 'react'
23

3-
import { queryKey, renderWithClient, sleep } from './utils'
4+
import { queryKey, renderWithClient, setActTimeout, sleep } from './utils'
45
import { useQueries, QueryClient, UseQueryResult, QueryCache } from '../..'
56

67
describe('useQueries', () => {
@@ -42,4 +43,196 @@ describe('useQueries', () => {
4243
expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }])
4344
expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }])
4445
})
46+
47+
it('should keep previous data if amount of queries is the same', async () => {
48+
const key1 = queryKey()
49+
const key2 = queryKey()
50+
const states: UseQueryResult[][] = []
51+
52+
function Page() {
53+
const [count, setCount] = React.useState(1)
54+
const result = useQueries([
55+
{
56+
queryKey: [key1, count],
57+
keepPreviousData: true,
58+
queryFn: async () => {
59+
await sleep(5)
60+
return count * 2
61+
},
62+
},
63+
{
64+
queryKey: [key2, count],
65+
keepPreviousData: true,
66+
queryFn: async () => {
67+
await sleep(10)
68+
return count * 5
69+
},
70+
},
71+
])
72+
states.push(result)
73+
74+
React.useEffect(() => {
75+
setActTimeout(() => {
76+
setCount(prev => prev + 1)
77+
}, 20)
78+
}, [])
79+
80+
return null
81+
}
82+
83+
renderWithClient(queryClient, <Page />)
84+
85+
await waitFor(() => expect(states.length).toBe(7))
86+
87+
expect(states[0]).toMatchObject([
88+
{
89+
status: 'loading',
90+
data: undefined,
91+
isPreviousData: false,
92+
isFetching: true,
93+
},
94+
{
95+
status: 'loading',
96+
data: undefined,
97+
isPreviousData: false,
98+
isFetching: true,
99+
},
100+
])
101+
expect(states[1]).toMatchObject([
102+
{ status: 'success', data: 2, isPreviousData: false, isFetching: false },
103+
{
104+
status: 'loading',
105+
data: undefined,
106+
isPreviousData: false,
107+
isFetching: true,
108+
},
109+
])
110+
expect(states[2]).toMatchObject([
111+
{ status: 'success', data: 2, isPreviousData: false, isFetching: false },
112+
{ status: 'success', data: 5, isPreviousData: false, isFetching: false },
113+
])
114+
expect(states[3]).toMatchObject([
115+
{ status: 'success', data: 2, isPreviousData: true, isFetching: true },
116+
{ status: 'success', data: 5, isPreviousData: true, isFetching: true },
117+
])
118+
expect(states[4]).toMatchObject([
119+
{ status: 'success', data: 2, isPreviousData: true, isFetching: true },
120+
{ status: 'success', data: 5, isPreviousData: true, isFetching: true },
121+
])
122+
expect(states[5]).toMatchObject([
123+
{ status: 'success', data: 4, isPreviousData: false, isFetching: false },
124+
{ status: 'success', data: 5, isPreviousData: true, isFetching: true },
125+
])
126+
expect(states[6]).toMatchObject([
127+
{ status: 'success', data: 4, isPreviousData: false, isFetching: false },
128+
{ status: 'success', data: 10, isPreviousData: false, isFetching: false },
129+
])
130+
})
131+
132+
it('should keep previous data for variable amounts of useQueries', async () => {
133+
const key = queryKey()
134+
const states: UseQueryResult[][] = []
135+
136+
function Page() {
137+
const [count, setCount] = React.useState(2)
138+
const result = useQueries(
139+
Array.from({ length: count }, (_, i) => ({
140+
queryKey: [key, count, i + 1],
141+
keepPreviousData: true,
142+
queryFn: async () => {
143+
await sleep(5 * (i + 1))
144+
return (i + 1) * count * 2
145+
},
146+
}))
147+
)
148+
149+
states.push(result)
150+
151+
React.useEffect(() => {
152+
setActTimeout(() => {
153+
setCount(prev => prev + 1)
154+
}, 20)
155+
}, [])
156+
157+
return null
158+
}
159+
160+
renderWithClient(queryClient, <Page />)
161+
162+
await waitFor(() => expect(states.length).toBe(8))
163+
164+
expect(states[0]).toMatchObject([
165+
{
166+
status: 'loading',
167+
data: undefined,
168+
isPreviousData: false,
169+
isFetching: true,
170+
},
171+
{
172+
status: 'loading',
173+
data: undefined,
174+
isPreviousData: false,
175+
isFetching: true,
176+
},
177+
])
178+
expect(states[1]).toMatchObject([
179+
{ status: 'success', data: 4, isPreviousData: false, isFetching: false },
180+
{
181+
status: 'loading',
182+
data: undefined,
183+
isPreviousData: false,
184+
isFetching: true,
185+
},
186+
])
187+
expect(states[2]).toMatchObject([
188+
{ status: 'success', data: 4, isPreviousData: false, isFetching: false },
189+
{ status: 'success', data: 8, isPreviousData: false, isFetching: false },
190+
])
191+
192+
expect(states[3]).toMatchObject([
193+
{ status: 'success', data: 4, isPreviousData: true, isFetching: true },
194+
{ status: 'success', data: 8, isPreviousData: true, isFetching: true },
195+
{
196+
status: 'loading',
197+
data: undefined,
198+
isPreviousData: false,
199+
isFetching: true,
200+
},
201+
])
202+
expect(states[4]).toMatchObject([
203+
{ status: 'success', data: 4, isPreviousData: true, isFetching: true },
204+
{ status: 'success', data: 8, isPreviousData: true, isFetching: true },
205+
{
206+
status: 'loading',
207+
data: undefined,
208+
isPreviousData: false,
209+
isFetching: true,
210+
},
211+
])
212+
expect(states[5]).toMatchObject([
213+
{ status: 'success', data: 6, isPreviousData: false, isFetching: false },
214+
{ status: 'success', data: 8, isPreviousData: true, isFetching: true },
215+
{
216+
status: 'loading',
217+
data: undefined,
218+
isPreviousData: false,
219+
isFetching: true,
220+
},
221+
])
222+
expect(states[6]).toMatchObject([
223+
{ status: 'success', data: 6, isPreviousData: false, isFetching: false },
224+
{ status: 'success', data: 12, isPreviousData: false, isFetching: false },
225+
{
226+
status: 'loading',
227+
data: undefined,
228+
isPreviousData: false,
229+
isFetching: true,
230+
},
231+
])
232+
expect(states[7]).toMatchObject([
233+
{ status: 'success', data: 6, isPreviousData: false, isFetching: false },
234+
{ status: 'success', data: 12, isPreviousData: false, isFetching: false },
235+
{ status: 'success', data: 18, isPreviousData: false, isFetching: false },
236+
])
237+
})
45238
})

0 commit comments

Comments
 (0)