Skip to content

Commit 9e0e60c

Browse files
authored
fix(experimental_createQueryPersister): Change persisterRestoreAll to restoreQueries with filter support (#9230)
* Add persisterRestoreByKey * Fix imports * Add missing return statement * Fix test * docs * Add extra test * Merge into single restoreQueries function
1 parent baaa310 commit 9e0e60c

File tree

4 files changed

+134
-18
lines changed

4 files changed

+134
-18
lines changed

docs/framework/react/plugins/createPersister.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,17 @@ This function can be used to sporadically clean up stoage from `expired`, `buste
111111
For this function to work, your storage must expose `entries` method that would return a `key-value tuple array`.
112112
For example `Object.entries(localStorage)` for `localStorage` or `entries` from `idb-keyval`.
113113

114-
### `persisterRestoreAll(queryClient: QueryClient): Promise<void>`
114+
### `restoreQueries(queryClient: QueryClient, filters): Promise<void>`
115115

116-
This function can be used to restore all queries that are currently stored by persister in one go.
117-
For example when your app is starting up in offline mode, or you want data from previous session to be immediately available without intermediate `loading` state.
116+
This function can be used to restore queries that are currently stored by persister.
117+
For example when your app is starting up in offline mode, or you want all or only specific data from previous session to be immediately available without intermediate `loading` state.
118+
119+
The filter object supports the following properties:
120+
121+
- `queryKey?: QueryKey`
122+
- Set this property to define a query key to match on.
123+
- `exact?: boolean`
124+
- If you don't want to search queries inclusively by query key, you can pass the `exact: true` option to return only the query with the exact query key you have passed.
118125

119126
For this function to work, your storage must expose `entries` method that would return a `key-value tuple array`.
120127
For example `Object.entries(localStorage)` for `localStorage` or `entries` from `idb-keyval`.

packages/query-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export { focusManager } from './focusManager'
1515
export { onlineManager } from './onlineManager'
1616
export {
1717
hashKey,
18+
partialMatchKey,
1819
replaceEqualDeep,
1920
isServer,
2021
matchQuery,

packages/query-persist-client-core/src/__tests__/createPersister.test.ts

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ describe('createPersister', () => {
497497
})
498498
})
499499

500-
describe('persisterRestoreAll', () => {
500+
describe('restoreQueries', () => {
501501
test('should properly clean storage from busted entries', async () => {
502502
const storage = getFreshStorage()
503503
const { persister, client, query, queryKey } = setupPersister(['foo'], {
@@ -513,11 +513,11 @@ describe('createPersister', () => {
513513

514514
expect(await storage.entries()).toHaveLength(1)
515515

516-
await persister.persisterRestoreAll(client)
516+
await persister.restoreQueries(client)
517517
expect(await storage.entries()).toHaveLength(0)
518518
})
519519

520-
test('should properly restore queries from cache', async () => {
520+
test('should properly restore queries from cache without filters', async () => {
521521
const storage = getFreshStorage()
522522
const { persister, client, queryKey } = setupPersister(['foo'], {
523523
storage,
@@ -530,10 +530,102 @@ describe('createPersister', () => {
530530
client.clear()
531531
expect(client.getQueryCache().getAll()).toHaveLength(0)
532532

533-
await persister.persisterRestoreAll(client)
533+
await persister.restoreQueries(client)
534+
expect(client.getQueryCache().getAll()).toHaveLength(1)
535+
536+
expect(client.getQueryData(queryKey)).toEqual('foo')
537+
})
538+
539+
test('should properly restore queries from cache', async () => {
540+
const storage = getFreshStorage()
541+
const { persister, client, queryKey } = setupPersister(['foo', 'bar'], {
542+
storage,
543+
})
544+
client.setQueryData(queryKey, 'foo')
545+
546+
await persister.persistQueryByKey(queryKey, client)
547+
548+
expect(await storage.entries()).toHaveLength(1)
549+
client.clear()
550+
expect(client.getQueryCache().getAll()).toHaveLength(0)
551+
552+
await persister.restoreQueries(client, { queryKey })
534553
expect(client.getQueryCache().getAll()).toHaveLength(1)
535554

536555
expect(client.getQueryData(queryKey)).toEqual('foo')
537556
})
557+
558+
test('should not restore queries from cache if there is no match', async () => {
559+
const storage = getFreshStorage()
560+
const { persister, client, queryKey } = setupPersister(['foo', 'bar'], {
561+
storage,
562+
})
563+
client.setQueryData(queryKey, 'foo')
564+
565+
await persister.persistQueryByKey(queryKey, client)
566+
567+
expect(await storage.entries()).toHaveLength(1)
568+
client.clear()
569+
expect(client.getQueryCache().getAll()).toHaveLength(0)
570+
571+
await persister.restoreQueries(client, { queryKey: ['bar'] })
572+
expect(client.getQueryCache().getAll()).toHaveLength(0)
573+
})
574+
575+
test('should properly restore queries from cache with partial match', async () => {
576+
const storage = getFreshStorage()
577+
const { persister, client, queryKey } = setupPersister(['foo', 'bar'], {
578+
storage,
579+
})
580+
client.setQueryData(queryKey, 'foo')
581+
582+
await persister.persistQueryByKey(queryKey, client)
583+
584+
expect(await storage.entries()).toHaveLength(1)
585+
client.clear()
586+
expect(client.getQueryCache().getAll()).toHaveLength(0)
587+
588+
await persister.restoreQueries(client, { queryKey: ['foo'] })
589+
expect(client.getQueryCache().getAll()).toHaveLength(1)
590+
591+
expect(client.getQueryData(queryKey)).toEqual('foo')
592+
})
593+
594+
test('should not restore queries from cache with exact match if there is no match', async () => {
595+
const storage = getFreshStorage()
596+
const { persister, client, queryKey } = setupPersister(['foo', 'bar'], {
597+
storage,
598+
})
599+
client.setQueryData(queryKey, 'foo')
600+
601+
await persister.persistQueryByKey(queryKey, client)
602+
603+
expect(await storage.entries()).toHaveLength(1)
604+
client.clear()
605+
expect(client.getQueryCache().getAll()).toHaveLength(0)
606+
607+
await persister.restoreQueries(client, { queryKey: ['foo'], exact: true })
608+
expect(client.getQueryCache().getAll()).toHaveLength(0)
609+
})
610+
611+
test('should restore queries from cache with exact match', async () => {
612+
const storage = getFreshStorage()
613+
const { persister, client, queryKey } = setupPersister(['foo', 'bar'], {
614+
storage,
615+
})
616+
client.setQueryData(queryKey, 'foo')
617+
618+
await persister.persistQueryByKey(queryKey, client)
619+
620+
expect(await storage.entries()).toHaveLength(1)
621+
client.clear()
622+
expect(client.getQueryCache().getAll()).toHaveLength(0)
623+
624+
await persister.restoreQueries(client, {
625+
queryKey: queryKey,
626+
exact: true,
627+
})
628+
expect(client.getQueryCache().getAll()).toHaveLength(1)
629+
})
538630
})
539631
})

packages/query-persist-client-core/src/createPersister.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { matchQuery } from '@tanstack/query-core'
1+
import { hashKey, matchQuery, partialMatchKey } from '@tanstack/query-core'
22
import type {
33
Query,
44
QueryClient,
@@ -240,7 +240,12 @@ export function experimental_createQueryPersister<TStorageValue = string>({
240240
}
241241
}
242242

243-
async function persisterRestoreAll(queryClient: QueryClient) {
243+
async function restoreQueries(
244+
queryClient: QueryClient,
245+
filters: Pick<QueryFilters, 'queryKey' | 'exact'> = {},
246+
): Promise<void> {
247+
const { exact, queryKey } = filters
248+
244249
if (storage?.entries) {
245250
const entries = await storage.entries()
246251
for (const [key, value] of entries) {
@@ -249,15 +254,26 @@ export function experimental_createQueryPersister<TStorageValue = string>({
249254

250255
if (isExpiredOrBusted(persistedQuery)) {
251256
await storage.removeItem(key)
252-
} else {
253-
queryClient.setQueryData(
254-
persistedQuery.queryKey,
255-
persistedQuery.state.data,
256-
{
257-
updatedAt: persistedQuery.state.dataUpdatedAt,
258-
},
259-
)
257+
continue
258+
}
259+
260+
if (queryKey) {
261+
if (exact) {
262+
if (persistedQuery.queryHash !== hashKey(queryKey)) {
263+
continue
264+
}
265+
} else if (!partialMatchKey(persistedQuery.queryKey, queryKey)) {
266+
continue
267+
}
260268
}
269+
270+
queryClient.setQueryData(
271+
persistedQuery.queryKey,
272+
persistedQuery.state.data,
273+
{
274+
updatedAt: persistedQuery.state.dataUpdatedAt,
275+
},
276+
)
261277
}
262278
}
263279
} else if (process.env.NODE_ENV === 'development') {
@@ -273,6 +289,6 @@ export function experimental_createQueryPersister<TStorageValue = string>({
273289
persistQueryByKey,
274290
retrieveQuery,
275291
persisterGc,
276-
persisterRestoreAll,
292+
restoreQueries,
277293
}
278294
}

0 commit comments

Comments
 (0)