Skip to content

Commit 44dc2c9

Browse files
authored
Merge branch 'main' into ci/change-version-commit-type-to-chore
2 parents 06f94c8 + b59925e commit 44dc2c9

File tree

95 files changed

+502
-125
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+502
-125
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
## ✅ Checklist
66

77
- [ ] I have followed the steps in the [Contributing guide](https://github.com/TanStack/query/blob/main/CONTRIBUTING.md).
8-
- [ ] I have tested this code locally with `pnpm test:pr`.
8+
- [ ] I have tested this code locally with `pnpm run test:pr`.
99

1010
## 🚀 Release Impact
1111

docs/framework/react/plugins/createPersister.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ export interface StoragePersisterOptions {
169169
* Storage key is a combination of prefix and query hash in a form of `prefix-queryHash`.
170170
*/
171171
prefix?: string
172+
/**
173+
* If set to `true`, the query will refetch on successful query restoration if the data is stale.
174+
* If set to `false`, the query will not refetch on successful query restoration.
175+
* If set to `'always'`, the query will always refetch on successful query restoration.
176+
* Defaults to `true`.
177+
*/
178+
refetchOnRestore?: boolean | 'always'
172179
/**
173180
* Filters to narrow down which Queries should be persisted.
174181
*/
@@ -191,5 +198,6 @@ The default options are:
191198
maxAge = 1000 * 60 * 60 * 24,
192199
serialize = JSON.stringify,
193200
deserialize = JSON.parse,
201+
refetchOnRestore = true,
194202
}
195203
```

docs/framework/vue/plugins/createPersister.md

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ bun add @tanstack/query-persist-client-core
3333

3434
- Import the `experimental_createQueryPersister` function
3535
- Create a new `experimental_createQueryPersister`
36-
- you can pass any `storage` to it that adheres to the `AsyncStorage` or `Storage` interface
36+
- you can pass any `storage` to it that adheres to the `AsyncStorage` interface
3737
- Pass that `persister` as an option to your Query. This can be done either by passing it to the `defaultOptions` of the `QueryClient` or to any `useQuery` hook instance.
3838
- If you pass this `persister` as `defaultOptions`, all queries will be persisted to the provided `storage`. You can additionally narrow this down by passing `filters`. In contrast to the `persistClient` plugin, this will not persist the whole query client as a single item, but each query separately. As a key, the query hash is used.
3939
- If you provide this `persister` to a single `useQuery` hook, only this Query will be persisted.
40+
- Note: `queryClient.setQueryData()` operations are not persisted, this means that if you perform an optimistic update and refresh the page before the query has been invalidated, your changes to the query data will be lost. See https://github.com/TanStack/query/issues/6310
4041

4142
This way, you do not need to store whole `QueryClient`, but choose what is worth to be persisted in your application. Each query is lazily restored (when the Query is first used) and persisted (after each run of the `queryFn`), so it does not need to be throttled. `staleTime` is also respected after restoring the Query, so if data is considered `stale`, it will be refetched immediately after restoring. If data is `fresh`, the `queryFn` will not run.
4243

@@ -65,6 +66,63 @@ const queryClient = new QueryClient({
6566

6667
The `createPersister` plugin technically wraps the `queryFn`, so it doesn't restore if the `queryFn` doesn't run. In that way, it acts as a caching layer between the Query and the network. Thus, the `networkMode` defaults to `'offlineFirst'` when a persister is used, so that restoring from the persistent storage can also happen even if there is no network connection.
6768

69+
## Additional utilities
70+
71+
Invoking `experimental_createQueryPersister` returns additional utilities in addition to `persisterFn` for easier implementation of userland functionalities.
72+
73+
### `persistQueryByKey(queryKey: QueryKey, queryClient: QueryClient): Promise<void>`
74+
75+
This function will persist `Query` to storage and key defined when creating persister.
76+
This utility might be used along `setQueryData` to persist optimistic update to storage without waiting for invalidation.
77+
78+
```tsx
79+
const persister = experimental_createQueryPersister({
80+
storage: AsyncStorage,
81+
maxAge: 1000 * 60 * 60 * 12, // 12 hours
82+
})
83+
84+
const queryClient = useQueryClient()
85+
86+
useMutation({
87+
mutationFn: updateTodo,
88+
onMutate: async (newTodo) => {
89+
...
90+
// Optimistically update to the new value
91+
queryClient.setQueryData(['todos'], (old) => [...old, newTodo])
92+
// And persist it to storage
93+
persister.persistQueryByKey(['todos'], queryClient)
94+
...
95+
},
96+
})
97+
```
98+
99+
### `retrieveQuery<T>(queryHash: string): Promise<T | undefined>`
100+
101+
This function would attempt to retrieve persisted query by `queryHash`.
102+
If `query` is `expired`, `busted` or `malformed` it would be removed from the storage instead, and `undefined` would be returned.
103+
104+
### `persisterGc(): Promise<void>`
105+
106+
This function can be used to sporadically clean up stoage from `expired`, `busted` or `malformed` entries.
107+
108+
For this function to work, your storage must expose `entries` method that would return a `key-value tuple array`.
109+
For example `Object.entries(localStorage)` for `localStorage` or `entries` from `idb-keyval`.
110+
111+
### `restoreQueries(queryClient: QueryClient, filters): Promise<void>`
112+
113+
This function can be used to restore queries that are currently stored by persister.
114+
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.
115+
116+
The filter object supports the following properties:
117+
118+
- `queryKey?: QueryKey`
119+
- Set this property to define a query key to match on.
120+
- `exact?: boolean`
121+
- 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.
122+
123+
For this function to work, your storage must expose `entries` method that would return a `key-value tuple array`.
124+
For example `Object.entries(localStorage)` for `localStorage` or `entries` from `idb-keyval`.
125+
68126
## API
69127

70128
### `experimental_createQueryPersister`
@@ -108,16 +166,24 @@ export interface StoragePersisterOptions {
108166
* Storage key is a combination of prefix and query hash in a form of `prefix-queryHash`.
109167
*/
110168
prefix?: string
169+
/**
170+
* If set to `true`, the query will refetch on successful query restoration if the data is stale.
171+
* If set to `false`, the query will not refetch on successful query restoration.
172+
* If set to `'always'`, the query will always refetch on successful query restoration.
173+
* Defaults to `true`.
174+
*/
175+
refetchOnRestore?: boolean | 'always'
111176
/**
112177
* Filters to narrow down which Queries should be persisted.
113178
*/
114179
filters?: QueryFilters
115180
}
116181

117-
interface AsyncStorage {
118-
getItem: (key: string) => Promise<string | undefined | null>
119-
setItem: (key: string, value: string) => Promise<unknown>
120-
removeItem: (key: string) => Promise<void>
182+
interface AsyncStorage<TStorageValue = string> {
183+
getItem: (key: string) => MaybePromise<TStorageValue | undefined | null>
184+
setItem: (key: string, value: TStorageValue) => MaybePromise<unknown>
185+
removeItem: (key: string) => MaybePromise<void>
186+
entries?: () => MaybePromise<Array<[key: string, value: TStorageValue]>>
121187
}
122188
```
123189

@@ -129,5 +195,6 @@ The default options are:
129195
maxAge = 1000 * 60 * 60 * 24,
130196
serialize = JSON.stringify,
131197
deserialize = JSON.parse,
198+
refetchOnRestore = true,
132199
}
133200
```

examples/angular/auto-refetching/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.90.2",
16+
"@tanstack/angular-query-experimental": "^5.90.3",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

examples/angular/basic-persister/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.90.2",
17-
"@tanstack/angular-query-persist-client": "^5.62.7",
18-
"@tanstack/query-async-storage-persister": "^5.90.2",
16+
"@tanstack/angular-query-experimental": "^5.90.3",
17+
"@tanstack/angular-query-persist-client": "^5.62.10",
18+
"@tanstack/query-async-storage-persister": "^5.90.5",
1919
"rxjs": "^7.8.2",
2020
"tslib": "^2.8.1",
2121
"zone.js": "0.15.0"

examples/angular/basic/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.90.2",
16+
"@tanstack/angular-query-experimental": "^5.90.3",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

examples/angular/devtools-panel/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
1616
"@angular/router": "^20.0.0",
17-
"@tanstack/angular-query-experimental": "^5.90.2",
17+
"@tanstack/angular-query-experimental": "^5.90.3",
1818
"rxjs": "^7.8.2",
1919
"tslib": "^2.8.1",
2020
"zone.js": "0.15.0"

examples/angular/infinite-query-with-max-pages/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.90.2",
16+
"@tanstack/angular-query-experimental": "^5.90.3",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

examples/angular/optimistic-updates/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@angular/core": "^20.0.0",
1515
"@angular/forms": "^20.0.0",
1616
"@angular/platform-browser": "^20.0.0",
17-
"@tanstack/angular-query-experimental": "^5.90.2",
17+
"@tanstack/angular-query-experimental": "^5.90.3",
1818
"rxjs": "^7.8.2",
1919
"tslib": "^2.8.1",
2020
"zone.js": "0.15.0"

examples/angular/pagination/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.90.2",
16+
"@tanstack/angular-query-experimental": "^5.90.3",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

0 commit comments

Comments
 (0)