Skip to content

Commit 7a628d6

Browse files
feat: asyncStoragePersistor for React Native (TanStack#2360)
1 parent ddbd755 commit 7a628d6

File tree

8 files changed

+166
-0
lines changed

8 files changed

+166
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"internal": true,
3+
"main": "../lib/createAsyncStoragePersistor-experimental/index.js",
4+
"module": "../es/createAsyncStoragePersistor-experimental/index.js",
5+
"types": "../types/createAsyncStoragePersistor-experimental/index.d.ts"
6+
}

docs/src/manifests/manifest.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,11 @@
312312
"path": "/plugins/createWebStoragePersistor",
313313
"editUrl": "/plugins/createWebStoragePersistor.md"
314314
},
315+
{
316+
"title": "createAsyncStoragePersistor (Experimental)",
317+
"path": "/plugins/createAsyncStoragePersistor",
318+
"editUrl": "/plugins/createAsyncStoragePersistor.md"
319+
},
315320
{
316321
"title": "broadcastQueryClient (Experimental)",
317322
"path": "/plugins/broadcastQueryClient",
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
id: createAsyncStoragePersistor
3+
title: createAsyncStoragePersistor for React Native (Experimental)
4+
---
5+
6+
> VERY IMPORTANT: This utility is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Use at your own risk. If you choose to rely on this in production in an experimental stage, please lock your version to a patch-level version to avoid unexpected breakages.
7+
8+
## Installation
9+
10+
This utility comes packaged with `react-query` and is available under the `react-query/createAsyncStoragePersistor-experimental` import.
11+
12+
## Usage
13+
14+
- Import the `createAsyncStoragePersistor` function
15+
- Create a new asyncStoragePersistor
16+
- Pass it to the [`persistQueryClient`](../persistQueryClient) function
17+
18+
```ts
19+
import { persistQueryClient } from 'react-query/persistQueryClient-experimental'
20+
import { createAsyncStoragePersistor } from 'react-query/createAsyncStoragePersistor-experimental'
21+
22+
const queryClient = new QueryClient({
23+
defaultOptions: {
24+
queries: {
25+
cacheTime: 1000 * 60 * 60 * 24, // 24 hours
26+
},
27+
},
28+
})
29+
30+
const asyncStoragePersistor = createAsyncStoragePersistor()
31+
32+
persistQueryClient({
33+
queryClient,
34+
persistor: asyncStoragePersistor,
35+
})
36+
```
37+
38+
## API
39+
40+
### `createAsyncStoragePersistor`
41+
42+
Call this function (with an optional options object) to create a asyncStoragePersistor that you can use later with `persisteQueryClient`.
43+
44+
```js
45+
createAsyncStoragePersistor(options?: CreateAsyncStoragePersistorOptions)
46+
```
47+
48+
### `Options`
49+
50+
An optional object of options:
51+
52+
```js
53+
interface CreateAsyncStoragePersistorOptions {
54+
/** The key to use when storing the cache to localstorage */
55+
asyncStorageKey?: string
56+
/** To avoid localstorage spamming,
57+
* pass a time in ms to throttle saving the cache to disk */
58+
throttleTime?: number
59+
}
60+
```
61+
62+
The default options are:
63+
64+
```js
65+
{
66+
asyncStorageKey = `REACT_QUERY_OFFLINE_CACHE`,
67+
throttleTime = 1000,
68+
}
69+
```

docs/src/pages/plugins/persistQueryClient.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ title: persistQueryClient (Experimental)
1010
## Officially Supported Persistors
1111

1212
- [createWebStoragePersistor (Experimental)](/plugins/createWebStoragePersistor)
13+
- [createAsyncStoragePersistor (Experimental)](/plugins/createAsyncStoragePersistor)
1314

1415
## Installation
1516

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"devtools",
5353
"persistQueryClient-experimental",
5454
"createWebStoragePersistor-experimental",
55+
"createAsyncStoragePersistor-experimental",
5556
"broadcastQueryClient-experimental",
5657
"lib",
5758
"react",

rollup.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ const inputSrcs = [
3030
'ReactQueryCreateWebStoragePersistorExperimental',
3131
'createWebStoragePersistor-experimental',
3232
],
33+
[
34+
'src/createAsyncStoragePersistor-experimental/index.ts',
35+
'ReactQueryCreateAsyncStoragePersistorExperimental',
36+
'createAsyncStoragePersistor-experimental',
37+
],
3338
[
3439
'src/broadcastQueryClient-experimental/index.ts',
3540
'ReactQueryBroadcastQueryClientExperimental',
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
interface AsyncStorage {
2+
getItem: (key: string) => Promise<string>
3+
setItem: (key: string, value: string) => Promise<unknown>
4+
removeItem: (key: string) => Promise<unknown>
5+
}
6+
7+
interface CreateAsyncStoragePersistorOptions {
8+
/** The storage client used for setting an retrieving items from cache */
9+
storage: AsyncStorage
10+
/** The key to use when storing the cache */
11+
key?: string
12+
/** To avoid spamming,
13+
* pass a time in ms to throttle saving the cache to disk */
14+
throttleTime?: number
15+
}
16+
17+
export const asyncStoragePersistor = ({
18+
storage,
19+
key = `REACT_QUERY_OFFLINE_CACHE`,
20+
throttleTime = 1000,
21+
}: CreateAsyncStoragePersistorOptions) => {
22+
return {
23+
persistClient: asyncThrottle(
24+
persistedClient => storage.setItem(key, JSON.stringify(persistedClient)),
25+
{ interval: throttleTime }
26+
),
27+
restoreClient: async () => {
28+
const cacheString = await storage.getItem(key)
29+
30+
if (!cacheString) {
31+
return
32+
}
33+
34+
return JSON.parse(cacheString)
35+
},
36+
removeClient: () => storage.removeItem(key),
37+
}
38+
}
39+
40+
function asyncThrottle<T>(
41+
func: (...args: ReadonlyArray<unknown>) => Promise<T>,
42+
{ interval = 1000, limit = 1 }: { interval?: number; limit?: number } = {}
43+
) {
44+
if (typeof func !== 'function') throw new Error('argument is not function.')
45+
const running = { current: false }
46+
let lastTime = 0
47+
let timeout: number
48+
const queue: Array<any[]> = []
49+
return (...args: any) =>
50+
(async () => {
51+
if (running.current) {
52+
lastTime = Date.now()
53+
if (queue.length > limit) {
54+
queue.shift()
55+
}
56+
57+
queue.push(args)
58+
clearTimeout(timeout)
59+
}
60+
if (Date.now() - lastTime > interval) {
61+
running.current = true
62+
await func(...args)
63+
lastTime = Date.now()
64+
running.current = false
65+
} else {
66+
if (queue.length > 0) {
67+
const lastArgs = queue[queue.length - 1]!
68+
timeout = setTimeout(async () => {
69+
if (!running.current) {
70+
running.current = true
71+
await func(...lastArgs)
72+
running.current = false
73+
}
74+
}, interval)
75+
}
76+
}
77+
})()
78+
}

tsconfig.types.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"./src/devtools/index.ts",
1616
"./src/persistQueryClient-experimental/index.ts",
1717
"./src/createWebStoragePersistor-experimental/index.ts",
18+
"./src/createAsyncStoragePersistor-experimental/index.ts",
1819
"./src/broadcastQueryClient-experimental/index.ts"
1920
],
2021
"exclude": ["./src/**/*"]

0 commit comments

Comments
 (0)