@tanstack/react-query 4.29.25 → 4.30.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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-query",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.30.0",
|
|
4
4
|
"description": "Hooks for managing, caching and syncing asynchronous and remote data in React",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"use-sync-external-store": "^1.2.0",
|
|
50
|
-
"@tanstack/query-core": "4.
|
|
50
|
+
"@tanstack/query-core": "4.30.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
@@ -1018,46 +1018,6 @@ describe('useQuery', () => {
|
|
|
1018
1018
|
expect(states[1]).toMatchObject({ data: 'test' })
|
|
1019
1019
|
})
|
|
1020
1020
|
|
|
1021
|
-
it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
|
|
1022
|
-
const key = queryKey()
|
|
1023
|
-
const states: UseQueryResult<string>[] = []
|
|
1024
|
-
|
|
1025
|
-
function Page() {
|
|
1026
|
-
const state = useQuery(key, () => ({ name: 'test' }), {
|
|
1027
|
-
select: (data) => data.name,
|
|
1028
|
-
notifyOnChangeProps: ['data'],
|
|
1029
|
-
})
|
|
1030
|
-
|
|
1031
|
-
states.push(state)
|
|
1032
|
-
|
|
1033
|
-
return (
|
|
1034
|
-
<div>
|
|
1035
|
-
<div>{state.data}</div>
|
|
1036
|
-
<button onClick={() => state.refetch()}>refetch</button>
|
|
1037
|
-
</div>
|
|
1038
|
-
)
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
const rendered = renderWithClient(queryClient, <Page />)
|
|
1042
|
-
|
|
1043
|
-
await waitFor(() => {
|
|
1044
|
-
rendered.getByText('test')
|
|
1045
|
-
})
|
|
1046
|
-
|
|
1047
|
-
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
1048
|
-
|
|
1049
|
-
await waitFor(() => {
|
|
1050
|
-
rendered.getByText('test')
|
|
1051
|
-
})
|
|
1052
|
-
|
|
1053
|
-
expect(states[0]).toMatchObject({ data: undefined })
|
|
1054
|
-
expect(states[1]).toMatchObject({ data: 'test' })
|
|
1055
|
-
|
|
1056
|
-
// make sure no additional renders happen
|
|
1057
|
-
await sleep(50)
|
|
1058
|
-
expect(states.length).toBe(2)
|
|
1059
|
-
})
|
|
1060
|
-
|
|
1061
1021
|
it('should throw an error when a selector throws', async () => {
|
|
1062
1022
|
const key = queryKey()
|
|
1063
1023
|
const states: UseQueryResult<string>[] = []
|
|
@@ -2223,60 +2183,267 @@ describe('useQuery', () => {
|
|
|
2223
2183
|
expect(states[2]).toMatchObject({ isStale: true })
|
|
2224
2184
|
})
|
|
2225
2185
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2186
|
+
describe('notifyOnChangeProps', () => {
|
|
2187
|
+
it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
|
|
2188
|
+
const key = queryKey()
|
|
2189
|
+
const states: UseQueryResult<string>[] = []
|
|
2229
2190
|
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2191
|
+
function Page() {
|
|
2192
|
+
const state = useQuery(
|
|
2193
|
+
key,
|
|
2194
|
+
async () => {
|
|
2195
|
+
await sleep(5)
|
|
2196
|
+
return 'test'
|
|
2197
|
+
},
|
|
2198
|
+
{
|
|
2199
|
+
notifyOnChangeProps: ['data'],
|
|
2200
|
+
},
|
|
2201
|
+
)
|
|
2241
2202
|
|
|
2242
|
-
|
|
2203
|
+
states.push(state)
|
|
2243
2204
|
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2205
|
+
return (
|
|
2206
|
+
<>
|
|
2207
|
+
<button
|
|
2208
|
+
onClick={async () => {
|
|
2209
|
+
await state.refetch()
|
|
2210
|
+
}}
|
|
2211
|
+
>
|
|
2212
|
+
refetch
|
|
2213
|
+
</button>
|
|
2253
2214
|
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2215
|
+
<div>{state.data}</div>
|
|
2216
|
+
</>
|
|
2217
|
+
)
|
|
2218
|
+
}
|
|
2258
2219
|
|
|
2259
|
-
|
|
2220
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2260
2221
|
|
|
2261
|
-
|
|
2262
|
-
|
|
2222
|
+
await waitFor(() => {
|
|
2223
|
+
rendered.getByText('test')
|
|
2224
|
+
})
|
|
2225
|
+
|
|
2226
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2227
|
+
|
|
2228
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2229
|
+
await sleep(20)
|
|
2230
|
+
|
|
2231
|
+
expect(states.length).toBe(2)
|
|
2232
|
+
expect(states[0]).toMatchObject({
|
|
2233
|
+
data: undefined,
|
|
2234
|
+
status: 'loading',
|
|
2235
|
+
isFetching: true,
|
|
2236
|
+
})
|
|
2237
|
+
expect(states[1]).toMatchObject({
|
|
2238
|
+
data: 'test',
|
|
2239
|
+
status: 'success',
|
|
2240
|
+
isFetching: false,
|
|
2241
|
+
})
|
|
2263
2242
|
})
|
|
2264
2243
|
|
|
2265
|
-
|
|
2244
|
+
it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
|
|
2245
|
+
const key = queryKey()
|
|
2246
|
+
const states: UseQueryResult<string>[] = []
|
|
2266
2247
|
|
|
2267
|
-
|
|
2268
|
-
|
|
2248
|
+
function Page() {
|
|
2249
|
+
const state = useQuery(key, () => ({ name: 'test' }), {
|
|
2250
|
+
select: (data) => data.name,
|
|
2251
|
+
notifyOnChangeProps: ['data'],
|
|
2252
|
+
})
|
|
2269
2253
|
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2254
|
+
states.push(state)
|
|
2255
|
+
|
|
2256
|
+
return (
|
|
2257
|
+
<div>
|
|
2258
|
+
<div>{state.data}</div>
|
|
2259
|
+
<button onClick={() => state.refetch()}>refetch</button>
|
|
2260
|
+
</div>
|
|
2261
|
+
)
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2265
|
+
|
|
2266
|
+
await waitFor(() => {
|
|
2267
|
+
rendered.getByText('test')
|
|
2268
|
+
})
|
|
2269
|
+
|
|
2270
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2271
|
+
|
|
2272
|
+
await waitFor(() => {
|
|
2273
|
+
rendered.getByText('test')
|
|
2274
|
+
})
|
|
2275
|
+
|
|
2276
|
+
expect(states[0]).toMatchObject({ data: undefined })
|
|
2277
|
+
expect(states[1]).toMatchObject({ data: 'test' })
|
|
2278
|
+
|
|
2279
|
+
// make sure no additional renders happen
|
|
2280
|
+
await sleep(50)
|
|
2281
|
+
expect(states.length).toBe(2)
|
|
2275
2282
|
})
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2283
|
+
|
|
2284
|
+
// See https://github.com/TanStack/query/discussions/5588
|
|
2285
|
+
describe('function', () => {
|
|
2286
|
+
it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
|
|
2287
|
+
const key = queryKey()
|
|
2288
|
+
const states: UseQueryResult<string>[] = []
|
|
2289
|
+
|
|
2290
|
+
function Page() {
|
|
2291
|
+
const state = useQuery(
|
|
2292
|
+
key,
|
|
2293
|
+
async () => {
|
|
2294
|
+
await sleep(5)
|
|
2295
|
+
return 'test'
|
|
2296
|
+
},
|
|
2297
|
+
{
|
|
2298
|
+
notifyOnChangeProps: () => ['data'],
|
|
2299
|
+
},
|
|
2300
|
+
)
|
|
2301
|
+
|
|
2302
|
+
states.push(state)
|
|
2303
|
+
|
|
2304
|
+
return (
|
|
2305
|
+
<>
|
|
2306
|
+
<button
|
|
2307
|
+
onClick={async () => {
|
|
2308
|
+
await state.refetch()
|
|
2309
|
+
}}
|
|
2310
|
+
>
|
|
2311
|
+
refetch
|
|
2312
|
+
</button>
|
|
2313
|
+
|
|
2314
|
+
<div>{state.data}</div>
|
|
2315
|
+
</>
|
|
2316
|
+
)
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2320
|
+
|
|
2321
|
+
await waitFor(() => {
|
|
2322
|
+
rendered.getByText('test')
|
|
2323
|
+
})
|
|
2324
|
+
|
|
2325
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2326
|
+
|
|
2327
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2328
|
+
await sleep(20)
|
|
2329
|
+
|
|
2330
|
+
expect(states.length).toBe(2)
|
|
2331
|
+
expect(states[0]).toMatchObject({
|
|
2332
|
+
data: undefined,
|
|
2333
|
+
status: 'loading',
|
|
2334
|
+
isFetching: true,
|
|
2335
|
+
})
|
|
2336
|
+
expect(states[1]).toMatchObject({
|
|
2337
|
+
data: 'test',
|
|
2338
|
+
status: 'success',
|
|
2339
|
+
isFetching: false,
|
|
2340
|
+
})
|
|
2341
|
+
})
|
|
2342
|
+
|
|
2343
|
+
it('should not re-render when change props are not actively being tracked', async () => {
|
|
2344
|
+
const key = queryKey()
|
|
2345
|
+
const states: UseQueryResult<string>[] = []
|
|
2346
|
+
|
|
2347
|
+
function Page() {
|
|
2348
|
+
const fetchCounterRef = React.useRef(0)
|
|
2349
|
+
const trackChangesRef = React.useRef(true)
|
|
2350
|
+
|
|
2351
|
+
const notifyOnChangeProps = React.useCallback(() => {
|
|
2352
|
+
return trackChangesRef.current ? 'all' : []
|
|
2353
|
+
}, [])
|
|
2354
|
+
|
|
2355
|
+
const state = useQuery(
|
|
2356
|
+
key,
|
|
2357
|
+
async () => {
|
|
2358
|
+
await sleep(5)
|
|
2359
|
+
fetchCounterRef.current++
|
|
2360
|
+
return `fetch counter: ${fetchCounterRef.current}`
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
notifyOnChangeProps,
|
|
2364
|
+
},
|
|
2365
|
+
)
|
|
2366
|
+
|
|
2367
|
+
states.push(state)
|
|
2368
|
+
|
|
2369
|
+
return (
|
|
2370
|
+
<>
|
|
2371
|
+
<button
|
|
2372
|
+
onClick={async () => {
|
|
2373
|
+
await state.refetch()
|
|
2374
|
+
}}
|
|
2375
|
+
>
|
|
2376
|
+
refetch
|
|
2377
|
+
</button>
|
|
2378
|
+
<button
|
|
2379
|
+
onClick={() => {
|
|
2380
|
+
trackChangesRef.current = true
|
|
2381
|
+
}}
|
|
2382
|
+
>
|
|
2383
|
+
enableTracking
|
|
2384
|
+
</button>
|
|
2385
|
+
<button
|
|
2386
|
+
onClick={() => {
|
|
2387
|
+
trackChangesRef.current = false
|
|
2388
|
+
}}
|
|
2389
|
+
>
|
|
2390
|
+
disableTracking
|
|
2391
|
+
</button>
|
|
2392
|
+
|
|
2393
|
+
<div>{state.data}</div>
|
|
2394
|
+
</>
|
|
2395
|
+
)
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
const rendered = renderWithClient(queryClient, <Page />)
|
|
2399
|
+
await waitFor(() => {
|
|
2400
|
+
rendered.getByText('fetch counter: 1')
|
|
2401
|
+
})
|
|
2402
|
+
|
|
2403
|
+
expect(states.length).toBe(2)
|
|
2404
|
+
expect(states[0]).toMatchObject({
|
|
2405
|
+
data: undefined,
|
|
2406
|
+
isFetching: true,
|
|
2407
|
+
status: 'loading',
|
|
2408
|
+
})
|
|
2409
|
+
expect(states[1]).toMatchObject({
|
|
2410
|
+
data: 'fetch counter: 1',
|
|
2411
|
+
status: 'success',
|
|
2412
|
+
isFetching: false,
|
|
2413
|
+
})
|
|
2414
|
+
|
|
2415
|
+
// disable tracking and refetch to check for re-renders
|
|
2416
|
+
fireEvent.click(
|
|
2417
|
+
rendered.getByRole('button', { name: 'disableTracking' }),
|
|
2418
|
+
)
|
|
2419
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2420
|
+
|
|
2421
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2422
|
+
await sleep(20)
|
|
2423
|
+
// still expect to only have two re-renders from the initial fetch
|
|
2424
|
+
expect(states.length).toBe(2)
|
|
2425
|
+
|
|
2426
|
+
// enable tracking and refetch to check for re-renders
|
|
2427
|
+
fireEvent.click(
|
|
2428
|
+
rendered.getByRole('button', { name: 'enableTracking' }),
|
|
2429
|
+
)
|
|
2430
|
+
fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
|
|
2431
|
+
|
|
2432
|
+
// sleep is required to make sure no additional renders happen after click
|
|
2433
|
+
await sleep(20)
|
|
2434
|
+
|
|
2435
|
+
expect(states.length).toBe(4)
|
|
2436
|
+
expect(states[2]).toMatchObject({
|
|
2437
|
+
data: 'fetch counter: 2',
|
|
2438
|
+
status: 'success',
|
|
2439
|
+
isFetching: true,
|
|
2440
|
+
})
|
|
2441
|
+
expect(states[3]).toMatchObject({
|
|
2442
|
+
data: 'fetch counter: 3',
|
|
2443
|
+
status: 'success',
|
|
2444
|
+
isFetching: false,
|
|
2445
|
+
})
|
|
2446
|
+
})
|
|
2280
2447
|
})
|
|
2281
2448
|
})
|
|
2282
2449
|
|