@tanstack/react-query 5.0.0-alpha.90 → 5.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/__tests__/useQuery.test.tsx +245 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-query",
|
|
3
|
-
"version": "5.0.0-
|
|
3
|
+
"version": "5.0.0-beta.0",
|
|
4
4
|
"description": "Hooks for managing, caching and syncing asynchronous and remote data in React",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"client-only": "0.0.1",
|
|
40
|
-
"@tanstack/query-core": "5.0.0-
|
|
40
|
+
"@tanstack/query-core": "5.0.0-beta.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/react": "^18.2.4",
|
|
@@ -802,48 +802,6 @@ describe('useQuery', () => {
|
|
|
802
802
|
expect(states[1]).toMatchObject({ data: 'test' })
|
|
803
803
|
})
|
|
804
804
|
|
|
805
|
-
it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
|
|
806
|
-
const key = queryKey()
|
|
807
|
-
const states: UseQueryResult<string>[] = []
|
|
808
|
-
|
|
809
|
-
function Page() {
|
|
810
|
-
const state = useQuery({
|
|
811
|
-
queryKey: key,
|
|
812
|
-
queryFn: () => ({ name: 'test' }),
|
|
813
|
-
select: (data) => data.name,
|
|
814
|
-
notifyOnChangeProps: ['data'],
|
|
815
|
-
})
|
|
816
|
-
|
|
817
|
-
states.push(state)
|
|
818
|
-
|
|
819
|
-
return (
|
|
820
|
-
<div>
|
|
821
|
-
<div>{state.data}</div>
|
|
822
|
-
<button onClick={() => state.refetch()}>refetch</button>
|
|
823
|
-
</div>
|
|
824
|
-
)
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
const rendered = renderWithClient(queryClient, <Page />)
|
|
828
|
-
|
|
829
|
-
await waitFor(() => {
|
|
830
|
-
rendered.getByText('test')
|
|
831
|
-
})
|
|
832
|
-
|
|
833
|
-
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
834
|
-
|
|
835
|
-
await waitFor(() => {
|
|
836
|
-
rendered.getByText('test')
|
|
837
|
-
})
|
|
838
|
-
|
|
839
|
-
expect(states[0]).toMatchObject({ data: undefined })
|
|
840
|
-
expect(states[1]).toMatchObject({ data: 'test' })
|
|
841
|
-
|
|
842
|
-
// make sure no additional renders happen
|
|
843
|
-
await sleep(50)
|
|
844
|
-
expect(states.length).toBe(2)
|
|
845
|
-
})
|
|
846
|
-
|
|
847
805
|
it('should throw an error when a selector throws', async () => {
|
|
848
806
|
const key = queryKey()
|
|
849
807
|
const states: UseQueryResult<string>[] = []
|
|
@@ -2210,59 +2168,263 @@ describe('useQuery', () => {
|
|
|
2210
2168
|
expect(states[2]).toMatchObject({ isStale: true })
|
|
2211
2169
|
})
|
|
2212
2170
|
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2171
|
+
describe('notifyOnChangeProps', () => {
|
|
2172
|
+
it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
|
|
2173
|
+
const key = queryKey()
|
|
2174
|
+
const states: UseQueryResult<string>[] = []
|
|
2216
2175
|
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
}
|
|
2176
|
+
function Page() {
|
|
2177
|
+
const state = useQuery({
|
|
2178
|
+
queryKey: key,
|
|
2179
|
+
queryFn: () => ({ name: 'test' }),
|
|
2180
|
+
select: (data) => data.name,
|
|
2181
|
+
notifyOnChangeProps: ['data'],
|
|
2182
|
+
})
|
|
2224
2183
|
|
|
2225
|
-
|
|
2226
|
-
})
|
|
2184
|
+
states.push(state)
|
|
2227
2185
|
|
|
2228
|
-
|
|
2186
|
+
return (
|
|
2187
|
+
<div>
|
|
2188
|
+
<div>{state.data}</div>
|
|
2189
|
+
<button onClick={() => state.refetch()}>refetch</button>
|
|
2190
|
+
</div>
|
|
2191
|
+
)
|
|
2192
|
+
}
|
|
2229
2193
|
|
|
2230
|
-
|
|
2231
|
-
<>
|
|
2232
|
-
<button
|
|
2233
|
-
onClick={async () => {
|
|
2234
|
-
await state.refetch()
|
|
2235
|
-
}}
|
|
2236
|
-
>
|
|
2237
|
-
refetch
|
|
2238
|
-
</button>
|
|
2194
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2239
2195
|
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
)
|
|
2243
|
-
}
|
|
2196
|
+
await waitFor(() => {
|
|
2197
|
+
rendered.getByText('test')
|
|
2198
|
+
})
|
|
2244
2199
|
|
|
2245
|
-
|
|
2200
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2246
2201
|
|
|
2247
|
-
|
|
2248
|
-
|
|
2202
|
+
await waitFor(() => {
|
|
2203
|
+
rendered.getByText('test')
|
|
2204
|
+
})
|
|
2205
|
+
|
|
2206
|
+
expect(states[0]).toMatchObject({ data: undefined })
|
|
2207
|
+
expect(states[1]).toMatchObject({ data: 'test' })
|
|
2208
|
+
|
|
2209
|
+
// make sure no additional renders happen
|
|
2210
|
+
await sleep(50)
|
|
2211
|
+
expect(states.length).toBe(2)
|
|
2249
2212
|
})
|
|
2213
|
+
it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
|
|
2214
|
+
const key = queryKey()
|
|
2215
|
+
const states: UseQueryResult<string>[] = []
|
|
2250
2216
|
|
|
2251
|
-
|
|
2217
|
+
function Page() {
|
|
2218
|
+
const state = useQuery({
|
|
2219
|
+
queryKey: key,
|
|
2220
|
+
queryFn: async () => {
|
|
2221
|
+
await sleep(5)
|
|
2222
|
+
return 'test'
|
|
2223
|
+
},
|
|
2252
2224
|
|
|
2253
|
-
|
|
2254
|
-
|
|
2225
|
+
notifyOnChangeProps: ['data'],
|
|
2226
|
+
})
|
|
2255
2227
|
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2228
|
+
states.push(state)
|
|
2229
|
+
|
|
2230
|
+
return (
|
|
2231
|
+
<>
|
|
2232
|
+
<button
|
|
2233
|
+
onClick={async () => {
|
|
2234
|
+
await state.refetch()
|
|
2235
|
+
}}
|
|
2236
|
+
>
|
|
2237
|
+
refetch
|
|
2238
|
+
</button>
|
|
2239
|
+
|
|
2240
|
+
<div>{state.data}</div>
|
|
2241
|
+
</>
|
|
2242
|
+
)
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2246
|
+
|
|
2247
|
+
await waitFor(() => {
|
|
2248
|
+
rendered.getByText('test')
|
|
2249
|
+
})
|
|
2250
|
+
|
|
2251
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2252
|
+
|
|
2253
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2254
|
+
await sleep(20)
|
|
2255
|
+
|
|
2256
|
+
expect(states.length).toBe(2)
|
|
2257
|
+
expect(states[0]).toMatchObject({
|
|
2258
|
+
data: undefined,
|
|
2259
|
+
status: 'pending',
|
|
2260
|
+
isFetching: true,
|
|
2261
|
+
})
|
|
2262
|
+
expect(states[1]).toMatchObject({
|
|
2263
|
+
data: 'test',
|
|
2264
|
+
status: 'success',
|
|
2265
|
+
isFetching: false,
|
|
2266
|
+
})
|
|
2261
2267
|
})
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2268
|
+
|
|
2269
|
+
// See https://github.com/TanStack/query/discussions/5588
|
|
2270
|
+
describe('function', () => {
|
|
2271
|
+
it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
|
|
2272
|
+
const key = queryKey()
|
|
2273
|
+
const states: UseQueryResult<string>[] = []
|
|
2274
|
+
|
|
2275
|
+
function Page() {
|
|
2276
|
+
const state = useQuery({
|
|
2277
|
+
queryKey: key,
|
|
2278
|
+
queryFn: async () => {
|
|
2279
|
+
await sleep(5)
|
|
2280
|
+
return 'test'
|
|
2281
|
+
},
|
|
2282
|
+
notifyOnChangeProps: () => ['data'],
|
|
2283
|
+
})
|
|
2284
|
+
|
|
2285
|
+
states.push(state)
|
|
2286
|
+
|
|
2287
|
+
return (
|
|
2288
|
+
<>
|
|
2289
|
+
<button
|
|
2290
|
+
onClick={async () => {
|
|
2291
|
+
await state.refetch()
|
|
2292
|
+
}}
|
|
2293
|
+
>
|
|
2294
|
+
refetch
|
|
2295
|
+
</button>
|
|
2296
|
+
|
|
2297
|
+
<div>{state.data}</div>
|
|
2298
|
+
</>
|
|
2299
|
+
)
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2303
|
+
|
|
2304
|
+
await waitFor(() => {
|
|
2305
|
+
rendered.getByText('test')
|
|
2306
|
+
})
|
|
2307
|
+
|
|
2308
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2309
|
+
|
|
2310
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2311
|
+
await sleep(20)
|
|
2312
|
+
|
|
2313
|
+
expect(states.length).toBe(2)
|
|
2314
|
+
expect(states[0]).toMatchObject({
|
|
2315
|
+
data: undefined,
|
|
2316
|
+
status: 'pending',
|
|
2317
|
+
isFetching: true,
|
|
2318
|
+
})
|
|
2319
|
+
expect(states[1]).toMatchObject({
|
|
2320
|
+
data: 'test',
|
|
2321
|
+
status: 'success',
|
|
2322
|
+
isFetching: false,
|
|
2323
|
+
})
|
|
2324
|
+
})
|
|
2325
|
+
|
|
2326
|
+
it('should not re-render when change props are not actively being tracked', async () => {
|
|
2327
|
+
const key = queryKey()
|
|
2328
|
+
const states: UseQueryResult<string>[] = []
|
|
2329
|
+
|
|
2330
|
+
function Page() {
|
|
2331
|
+
const fetchCounterRef = React.useRef(0)
|
|
2332
|
+
const trackChangesRef = React.useRef(true)
|
|
2333
|
+
|
|
2334
|
+
const notifyOnChangeProps = React.useCallback(() => {
|
|
2335
|
+
return trackChangesRef.current ? 'all' : []
|
|
2336
|
+
}, [])
|
|
2337
|
+
|
|
2338
|
+
const state = useQuery({
|
|
2339
|
+
queryKey: key,
|
|
2340
|
+
queryFn: async () => {
|
|
2341
|
+
await sleep(5)
|
|
2342
|
+
fetchCounterRef.current++
|
|
2343
|
+
return `fetch counter: ${fetchCounterRef.current}`
|
|
2344
|
+
},
|
|
2345
|
+
notifyOnChangeProps,
|
|
2346
|
+
})
|
|
2347
|
+
|
|
2348
|
+
states.push(state)
|
|
2349
|
+
|
|
2350
|
+
return (
|
|
2351
|
+
<>
|
|
2352
|
+
<button
|
|
2353
|
+
onClick={async () => {
|
|
2354
|
+
await state.refetch()
|
|
2355
|
+
}}
|
|
2356
|
+
>
|
|
2357
|
+
refetch
|
|
2358
|
+
</button>
|
|
2359
|
+
<button
|
|
2360
|
+
onClick={() => {
|
|
2361
|
+
trackChangesRef.current = true
|
|
2362
|
+
}}
|
|
2363
|
+
>
|
|
2364
|
+
enableTracking
|
|
2365
|
+
</button>
|
|
2366
|
+
<button
|
|
2367
|
+
onClick={() => {
|
|
2368
|
+
trackChangesRef.current = false
|
|
2369
|
+
}}
|
|
2370
|
+
>
|
|
2371
|
+
disableTracking
|
|
2372
|
+
</button>
|
|
2373
|
+
|
|
2374
|
+
<div>{state.data}</div>
|
|
2375
|
+
</>
|
|
2376
|
+
)
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2380
|
+
await waitFor(() => {
|
|
2381
|
+
rendered.getByText('fetch counter: 1')
|
|
2382
|
+
})
|
|
2383
|
+
|
|
2384
|
+
expect(states.length).toBe(2)
|
|
2385
|
+
expect(states[0]).toMatchObject({
|
|
2386
|
+
data: undefined,
|
|
2387
|
+
isFetching: true,
|
|
2388
|
+
status: 'pending',
|
|
2389
|
+
})
|
|
2390
|
+
expect(states[1]).toMatchObject({
|
|
2391
|
+
data: 'fetch counter: 1',
|
|
2392
|
+
status: 'success',
|
|
2393
|
+
isFetching: false,
|
|
2394
|
+
})
|
|
2395
|
+
|
|
2396
|
+
// disable tracking and refetch to check for re-renders
|
|
2397
|
+
fireEvent.click(
|
|
2398
|
+
rendered.getByRole('button', { name: 'disableTracking' }),
|
|
2399
|
+
)
|
|
2400
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2401
|
+
|
|
2402
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2403
|
+
await sleep(20)
|
|
2404
|
+
// still expect to only have two re-renders from the initial fetch
|
|
2405
|
+
expect(states.length).toBe(2)
|
|
2406
|
+
|
|
2407
|
+
// enable tracking and refetch to check for re-renders
|
|
2408
|
+
fireEvent.click(
|
|
2409
|
+
rendered.getByRole('button', { name: 'enableTracking' }),
|
|
2410
|
+
)
|
|
2411
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2412
|
+
|
|
2413
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2414
|
+
await sleep(20)
|
|
2415
|
+
|
|
2416
|
+
expect(states.length).toBe(4)
|
|
2417
|
+
expect(states[2]).toMatchObject({
|
|
2418
|
+
data: 'fetch counter: 2',
|
|
2419
|
+
status: 'success',
|
|
2420
|
+
isFetching: true,
|
|
2421
|
+
})
|
|
2422
|
+
expect(states[3]).toMatchObject({
|
|
2423
|
+
data: 'fetch counter: 3',
|
|
2424
|
+
status: 'success',
|
|
2425
|
+
isFetching: false,
|
|
2426
|
+
})
|
|
2427
|
+
})
|
|
2266
2428
|
})
|
|
2267
2429
|
})
|
|
2268
2430
|
|