@qiaopeng/tanstack-query-plus 0.5.8 → 0.5.10
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/README.md +155 -95
- package/dist/{types/base.d.ts → base-CHnwqfyz.d.cts} +12 -14
- package/dist/base-CHnwqfyz.d.ts +52 -0
- package/dist/chunk-52ZO6Y67.cjs +1121 -0
- package/dist/chunk-52ZO6Y67.cjs.map +1 -0
- package/dist/chunk-5J6OXSLW.cjs +36 -0
- package/dist/chunk-5J6OXSLW.cjs.map +1 -0
- package/dist/chunk-6MAYHLTE.cjs +310 -0
- package/dist/chunk-6MAYHLTE.cjs.map +1 -0
- package/dist/chunk-ADS2QTMP.js +144 -0
- package/dist/chunk-ADS2QTMP.js.map +1 -0
- package/dist/chunk-APXNNHBD.cjs +374 -0
- package/dist/chunk-APXNNHBD.cjs.map +1 -0
- package/dist/chunk-AXMWOGNX.js +134 -0
- package/dist/chunk-AXMWOGNX.js.map +1 -0
- package/dist/chunk-B4KO3K3E.cjs +521 -0
- package/dist/chunk-B4KO3K3E.cjs.map +1 -0
- package/dist/chunk-BK3OTIBD.cjs +15 -0
- package/dist/chunk-BK3OTIBD.cjs.map +1 -0
- package/dist/chunk-BYAOQALW.js +13 -0
- package/dist/chunk-BYAOQALW.js.map +1 -0
- package/dist/chunk-CRTVS7VI.cjs +162 -0
- package/dist/chunk-CRTVS7VI.cjs.map +1 -0
- package/dist/chunk-EXITP7QO.js +288 -0
- package/dist/chunk-EXITP7QO.js.map +1 -0
- package/dist/chunk-GMO3PRZZ.js +565 -0
- package/dist/chunk-GMO3PRZZ.js.map +1 -0
- package/dist/chunk-HRO2DWKZ.js +12 -0
- package/dist/chunk-HRO2DWKZ.js.map +1 -0
- package/dist/chunk-JHDKUQSB.js +1069 -0
- package/dist/chunk-JHDKUQSB.js.map +1 -0
- package/dist/chunk-JN2Y6RSY.js +23 -0
- package/dist/chunk-JN2Y6RSY.js.map +1 -0
- package/dist/chunk-JRJSKRZW.cjs +29 -0
- package/dist/chunk-JRJSKRZW.cjs.map +1 -0
- package/dist/chunk-KC62H4VJ.js +123 -0
- package/dist/chunk-KC62H4VJ.js.map +1 -0
- package/dist/chunk-LHEHSLD5.js +31 -0
- package/dist/chunk-LHEHSLD5.js.map +1 -0
- package/dist/chunk-N4264P7N.cjs +156 -0
- package/dist/chunk-N4264P7N.cjs.map +1 -0
- package/dist/chunk-NF5QYPYC.cjs +133 -0
- package/dist/chunk-NF5QYPYC.cjs.map +1 -0
- package/dist/chunk-OFLCHKKE.cjs +28 -0
- package/dist/chunk-OFLCHKKE.cjs.map +1 -0
- package/dist/chunk-PCNSWVA5.cjs +602 -0
- package/dist/chunk-PCNSWVA5.cjs.map +1 -0
- package/dist/chunk-STOMAA2X.js +85 -0
- package/dist/chunk-STOMAA2X.js.map +1 -0
- package/dist/chunk-UVF5S6LX.cjs +15 -0
- package/dist/chunk-UVF5S6LX.cjs.map +1 -0
- package/dist/chunk-WEIXCDCA.cjs +90 -0
- package/dist/chunk-WEIXCDCA.cjs.map +1 -0
- package/dist/chunk-X3ZTSLBQ.js +355 -0
- package/dist/chunk-X3ZTSLBQ.js.map +1 -0
- package/dist/chunk-YEV73J4J.js +504 -0
- package/dist/chunk-YEV73J4J.js.map +1 -0
- package/dist/chunk-YW5PNTRU.cjs +14 -0
- package/dist/chunk-YW5PNTRU.cjs.map +1 -0
- package/dist/chunk-ZNXSWUIS.js +12 -0
- package/dist/chunk-ZNXSWUIS.js.map +1 -0
- package/dist/chunk-ZUEMBY4W.js +21 -0
- package/dist/chunk-ZUEMBY4W.js.map +1 -0
- package/dist/components/index.cjs +60 -0
- package/dist/components/index.cjs.map +1 -0
- package/dist/components/index.d.cts +43 -0
- package/dist/components/index.d.ts +43 -4
- package/dist/components/index.js +3 -4
- package/dist/components/index.js.map +1 -0
- package/dist/core/devtools.cjs +25 -0
- package/dist/core/devtools.cjs.map +1 -0
- package/dist/core/devtools.d.cts +17 -0
- package/dist/core/devtools.d.ts +9 -7
- package/dist/core/devtools.js +4 -16
- package/dist/core/devtools.js.map +1 -0
- package/dist/core/index.cjs +220 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +196 -0
- package/dist/core/index.d.ts +196 -9
- package/dist/core/index.js +7 -8
- package/dist/core/index.js.map +1 -0
- package/dist/features/index.cjs +76 -0
- package/dist/features/index.cjs.map +1 -0
- package/dist/features/index.d.cts +86 -0
- package/dist/features/index.d.ts +86 -4
- package/dist/features/index.js +7 -3
- package/dist/features/index.js.map +1 -0
- package/dist/hooks/index.cjs +209 -0
- package/dist/hooks/index.cjs.map +1 -0
- package/dist/hooks/index.d.cts +377 -0
- package/dist/hooks/index.d.ts +377 -10
- package/dist/hooks/index.js +8 -9
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useInViewPrefetch.cjs +33 -0
- package/dist/hooks/useInViewPrefetch.cjs.map +1 -0
- package/dist/hooks/useInViewPrefetch.d.cts +12 -0
- package/dist/hooks/useInViewPrefetch.d.ts +6 -4
- package/dist/hooks/useInViewPrefetch.js +30 -20
- package/dist/hooks/useInViewPrefetch.js.map +1 -0
- package/dist/index.cjs +811 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +36 -0
- package/dist/index.d.ts +36 -8
- package/dist/index.js +89 -7
- package/dist/index.js.map +1 -0
- package/dist/{types/offline.d.ts → offline-DVPtgoAS.d.ts} +15 -13
- package/dist/offline-xxeA_-6V.d.cts +99 -0
- package/dist/persistence-MRtkmhqq.d.cts +216 -0
- package/dist/persistence-tIrEb0pR.d.ts +216 -0
- package/dist/react-query/index.cjs +52 -0
- package/dist/react-query/index.cjs.map +1 -0
- package/dist/react-query/index.d.cts +1 -0
- package/dist/react-query/index.d.ts +1 -3
- package/dist/react-query/index.js +3 -1
- package/dist/react-query/index.js.map +1 -0
- package/dist/types/index.cjs +43 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +157 -0
- package/dist/types/index.d.ts +116 -12
- package/dist/types/index.js +6 -8
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.cjs +234 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +272 -0
- package/dist/utils/index.d.ts +272 -10
- package/dist/utils/index.js +9 -9
- package/dist/utils/index.js.map +1 -0
- package/package.json +13 -3
- package/dist/PersistQueryClientProvider.d.ts +0 -22
- package/dist/PersistQueryClientProvider.d.ts.map +0 -1
- package/dist/PersistQueryClientProvider.js +0 -47
- package/dist/components/LoadingFallback.d.ts +0 -16
- package/dist/components/LoadingFallback.d.ts.map +0 -1
- package/dist/components/LoadingFallback.js +0 -27
- package/dist/components/QueryErrorBoundary.d.ts +0 -12
- package/dist/components/QueryErrorBoundary.d.ts.map +0 -1
- package/dist/components/QueryErrorBoundary.js +0 -9
- package/dist/components/SuspenseWrapper.d.ts +0 -14
- package/dist/components/SuspenseWrapper.d.ts.map +0 -1
- package/dist/components/SuspenseWrapper.js +0 -9
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/internal/ErrorBoundary.d.ts +0 -27
- package/dist/components/internal/ErrorBoundary.d.ts.map +0 -1
- package/dist/components/internal/ErrorBoundary.js +0 -37
- package/dist/core/config.d.ts +0 -80
- package/dist/core/config.d.ts.map +0 -1
- package/dist/core/config.js +0 -321
- package/dist/core/devtools.d.ts.map +0 -1
- package/dist/core/env.d.ts +0 -4
- package/dist/core/env.d.ts.map +0 -1
- package/dist/core/env.js +0 -26
- package/dist/core/focusManager.d.ts +0 -33
- package/dist/core/focusManager.d.ts.map +0 -1
- package/dist/core/focusManager.js +0 -122
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/keys.d.ts +0 -59
- package/dist/core/keys.d.ts.map +0 -1
- package/dist/core/keys.js +0 -107
- package/dist/core/queryOptions.d.ts +0 -19
- package/dist/core/queryOptions.d.ts.map +0 -1
- package/dist/core/queryOptions.js +0 -44
- package/dist/features/index.d.ts.map +0 -1
- package/dist/features/offline.d.ts +0 -48
- package/dist/features/offline.d.ts.map +0 -1
- package/dist/features/offline.js +0 -269
- package/dist/features/persistence.d.ts +0 -36
- package/dist/features/persistence.d.ts.map +0 -1
- package/dist/features/persistence.js +0 -186
- package/dist/hooks/batchQueries.d.ts +0 -129
- package/dist/hooks/batchQueries.d.ts.map +0 -1
- package/dist/hooks/batchQueries.js +0 -301
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/useDataGuardMutation.d.ts +0 -39
- package/dist/hooks/useDataGuardMutation.d.ts.map +0 -1
- package/dist/hooks/useDataGuardMutation.js +0 -151
- package/dist/hooks/useDataGuardQuery.d.ts +0 -28
- package/dist/hooks/useDataGuardQuery.d.ts.map +0 -1
- package/dist/hooks/useDataGuardQuery.js +0 -71
- package/dist/hooks/useFocusManager.d.ts +0 -41
- package/dist/hooks/useFocusManager.d.ts.map +0 -1
- package/dist/hooks/useFocusManager.js +0 -109
- package/dist/hooks/useInViewPrefetch.d.ts.map +0 -1
- package/dist/hooks/useInfiniteQuery.d.ts +0 -33
- package/dist/hooks/useInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useInfiniteQuery.js +0 -61
- package/dist/hooks/useMutation.d.ts +0 -25
- package/dist/hooks/useMutation.d.ts.map +0 -1
- package/dist/hooks/useMutation.js +0 -180
- package/dist/hooks/usePrefetch.d.ts +0 -54
- package/dist/hooks/usePrefetch.d.ts.map +0 -1
- package/dist/hooks/usePrefetch.js +0 -229
- package/dist/hooks/useQuery.d.ts +0 -21
- package/dist/hooks/useQuery.d.ts.map +0 -1
- package/dist/hooks/useQuery.js +0 -46
- package/dist/hooks/useSuspenseQuery.d.ts +0 -11
- package/dist/hooks/useSuspenseQuery.d.ts.map +0 -1
- package/dist/hooks/useSuspenseQuery.js +0 -19
- package/dist/index.d.ts.map +0 -1
- package/dist/react-query/index.d.ts.map +0 -1
- package/dist/types/base.d.ts.map +0 -1
- package/dist/types/base.js +0 -26
- package/dist/types/dataGuard.d.ts +0 -69
- package/dist/types/dataGuard.d.ts.map +0 -1
- package/dist/types/dataGuard.js +0 -10
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/infinite.d.ts +0 -39
- package/dist/types/infinite.d.ts.map +0 -1
- package/dist/types/infinite.js +0 -1
- package/dist/types/offline.d.ts.map +0 -1
- package/dist/types/offline.js +0 -8
- package/dist/types/optimistic.d.ts +0 -126
- package/dist/types/optimistic.d.ts.map +0 -1
- package/dist/types/optimistic.js +0 -7
- package/dist/types/persistence.d.ts +0 -9
- package/dist/types/persistence.d.ts.map +0 -1
- package/dist/types/persistence.js +0 -1
- package/dist/types/selectors.d.ts +0 -11
- package/dist/types/selectors.d.ts.map +0 -1
- package/dist/types/selectors.js +0 -1
- package/dist/types/suspense.d.ts +0 -67
- package/dist/types/suspense.d.ts.map +0 -1
- package/dist/types/suspense.js +0 -1
- package/dist/utils/dataGuard.d.ts +0 -37
- package/dist/utils/dataGuard.d.ts.map +0 -1
- package/dist/utils/dataGuard.js +0 -257
- package/dist/utils/fieldMapper.d.ts +0 -9
- package/dist/utils/fieldMapper.d.ts.map +0 -1
- package/dist/utils/fieldMapper.js +0 -27
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/network.d.ts +0 -9
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js +0 -43
- package/dist/utils/optimisticUtils.d.ts +0 -45
- package/dist/utils/optimisticUtils.d.ts.map +0 -1
- package/dist/utils/optimisticUtils.js +0 -116
- package/dist/utils/placeholderData.d.ts +0 -3
- package/dist/utils/placeholderData.d.ts.map +0 -1
- package/dist/utils/placeholderData.js +0 -28
- package/dist/utils/prefetchManager.d.ts +0 -111
- package/dist/utils/prefetchManager.d.ts.map +0 -1
- package/dist/utils/prefetchManager.js +0 -246
- package/dist/utils/queryKey.d.ts +0 -24
- package/dist/utils/queryKey.d.ts.map +0 -1
- package/dist/utils/queryKey.js +0 -77
- package/dist/utils/selectors.d.ts +0 -30
- package/dist/utils/selectors.d.ts.map +0 -1
- package/dist/utils/selectors.js +0 -18
- package/dist/utils/storage.d.ts +0 -7
- package/dist/utils/storage.d.ts.map +0 -1
- package/dist/utils/storage.js +0 -84
package/README.md
CHANGED
|
@@ -12,15 +12,16 @@
|
|
|
12
12
|
6. [第四步:管理 Query Key](#6-第四步管理-query-key)
|
|
13
13
|
7. [第五步:数据变更与乐观更新](#7-第五步数据变更与乐观更新)
|
|
14
14
|
8. [第六步:无限滚动与分页](#8-第六步无限滚动与分页)
|
|
15
|
-
9. [
|
|
16
|
-
10. [
|
|
17
|
-
11. [
|
|
18
|
-
12. [
|
|
19
|
-
13. [
|
|
20
|
-
14. [
|
|
21
|
-
15. [
|
|
22
|
-
16. [
|
|
23
|
-
17. [
|
|
15
|
+
9. [第七步:全局状态与 Mutation 监控](#9-第七步全局状态与-mutation-监控)
|
|
16
|
+
10. [第八步:批量查询与仪表盘](#10-第八步批量查询与仪表盘)
|
|
17
|
+
11. [第九步:智能预取](#11-第九步智能预取)
|
|
18
|
+
12. [第十步:Suspense 模式与 SSR](#12-第十步suspense-模式与-ssr)
|
|
19
|
+
13. [第十一步:离线支持与持久化](#13-第十一步离线支持与持久化)
|
|
20
|
+
14. [第十二步:数据防护与安全](#14-第十二步数据防护与安全)
|
|
21
|
+
15. [第十三步:焦点管理](#15-第十三步焦点管理)
|
|
22
|
+
16. [第十四步:工具函数与选择器](#16-第十四步工具函数与选择器)
|
|
23
|
+
17. [最佳实践与常见问题](#17-最佳实践与常见问题)
|
|
24
|
+
18. [API 索引](#18-api-索引)
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -295,9 +296,11 @@ function App() {
|
|
|
295
296
|
retryDelay: exponentialBackoff,
|
|
296
297
|
refetchOnWindowFocus: true,
|
|
297
298
|
refetchOnReconnect: true,
|
|
299
|
+
refetchOnMount: true,
|
|
298
300
|
},
|
|
299
301
|
mutations: {
|
|
300
302
|
retry: 0, // Mutation 默认不重试
|
|
303
|
+
retryDelay: exponentialBackoff,
|
|
301
304
|
gcTime: 600000,
|
|
302
305
|
}
|
|
303
306
|
}
|
|
@@ -325,7 +328,13 @@ function App() {
|
|
|
325
328
|
import { getConfigByEnvironment } from '@qiaopeng/tanstack-query-plus/core'
|
|
326
329
|
|
|
327
330
|
// 根据环境自动选择配置
|
|
328
|
-
const
|
|
331
|
+
const env =
|
|
332
|
+
process.env.NODE_ENV === 'production'
|
|
333
|
+
? 'production'
|
|
334
|
+
: process.env.NODE_ENV === 'test'
|
|
335
|
+
? 'test'
|
|
336
|
+
: 'development'
|
|
337
|
+
const config = getConfigByEnvironment(env)
|
|
329
338
|
const queryClient = new QueryClient({ defaultOptions: config })
|
|
330
339
|
```
|
|
331
340
|
|
|
@@ -340,6 +349,8 @@ const queryClient = new QueryClient({ defaultOptions: config })
|
|
|
340
349
|
|
|
341
350
|
*智能重试:4XX 不重试,5XX 最多 1 次,网络错误最多 2 次
|
|
342
351
|
|
|
352
|
+
补充:还支持 `getConfigByEnvironment('longCache')` 与 `getConfigByEnvironment('realtime')` 两种预设,分别适用于“长缓存”与“高实时”场景。
|
|
353
|
+
|
|
343
354
|
### 3.5 自定义重试策略
|
|
344
355
|
|
|
345
356
|
如果默认的重试策略不满足你的需求,可以使用 `createSafeRetryStrategy` 和 `createErrorSafeConfig` 来自定义:
|
|
@@ -1129,13 +1140,21 @@ batchDelete.mutate(['id1', 'id2', 'id3'])
|
|
|
1129
1140
|
2. 使用离线队列在恢复网络后批量执行(稳健且可持久化):
|
|
1130
1141
|
|
|
1131
1142
|
```tsx
|
|
1132
|
-
import { createOfflineQueueManager, mutationRegistry } from '@qiaopeng/tanstack-query-plus/features'
|
|
1143
|
+
import { createOfflineQueueManager, mutationRegistry, serializeMutationKey } from '@qiaopeng/tanstack-query-plus/features'
|
|
1144
|
+
import { MutationOperationType } from '@qiaopeng/tanstack-query-plus/types'
|
|
1133
1145
|
|
|
1134
1146
|
const queue = createOfflineQueueManager({ storageKey: 'todo-ops', concurrency: 3 })
|
|
1135
1147
|
|
|
1136
|
-
function registerDelete(id: string) {
|
|
1137
|
-
|
|
1138
|
-
|
|
1148
|
+
async function registerDelete(id: string) {
|
|
1149
|
+
const key = serializeMutationKey(['todos', 'delete', id])
|
|
1150
|
+
mutationRegistry.register(key, () => api.deleteTodo(id))
|
|
1151
|
+
await queue.add({
|
|
1152
|
+
type: MutationOperationType.DELETE,
|
|
1153
|
+
mutationKey: ['todos', 'delete', id],
|
|
1154
|
+
variables: { id },
|
|
1155
|
+
mutationFn: () => api.deleteTodo(id),
|
|
1156
|
+
priority: 1
|
|
1157
|
+
})
|
|
1139
1158
|
}
|
|
1140
1159
|
```
|
|
1141
1160
|
|
|
@@ -1613,7 +1632,62 @@ const result = useEnhancedInfiniteQuery(customOptions)
|
|
|
1613
1632
|
|
|
1614
1633
|
---
|
|
1615
1634
|
|
|
1616
|
-
## 9.
|
|
1635
|
+
## 9. 第七步:全局状态与 Mutation 监控
|
|
1636
|
+
|
|
1637
|
+
在复杂的应用中,你可能需要监控全局的加载状态,或者获取正在进行的 Mutation 进度。本库补全了 v5 的状态监控 Hooks。
|
|
1638
|
+
|
|
1639
|
+
### 9.1 监控全局加载状态
|
|
1640
|
+
|
|
1641
|
+
使用 `useIsFetching` 和 `useIsMutating` 可以实时感知后台活动:
|
|
1642
|
+
|
|
1643
|
+
```tsx
|
|
1644
|
+
import { useIsFetching, useIsMutating } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
1645
|
+
|
|
1646
|
+
function GlobalLoadingIndicator() {
|
|
1647
|
+
const isFetching = useIsFetching() // 正在进行的 Query 数量
|
|
1648
|
+
const isMutating = useIsMutating() // 正在进行的 Mutation 数量
|
|
1649
|
+
|
|
1650
|
+
if (!isFetching && !isMutating) return null
|
|
1651
|
+
|
|
1652
|
+
return (
|
|
1653
|
+
<div className="fixed top-0 right-0 p-4">
|
|
1654
|
+
{isFetching > 0 && <span>数据加载中...</span>}
|
|
1655
|
+
{isMutating > 0 && <span>后台同步中...</span>}
|
|
1656
|
+
</div>
|
|
1657
|
+
)
|
|
1658
|
+
}
|
|
1659
|
+
```
|
|
1660
|
+
|
|
1661
|
+
### 9.2 监控 Mutation 详细状态
|
|
1662
|
+
|
|
1663
|
+
`useMutationState` 允许你订阅 Mutation 缓存,获取特定任务的进度或结果:
|
|
1664
|
+
|
|
1665
|
+
```tsx
|
|
1666
|
+
import { useMutationState } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
1667
|
+
|
|
1668
|
+
function UploadManager() {
|
|
1669
|
+
// 获取所有 ['upload'] 相关的 mutation 状态
|
|
1670
|
+
const uploads = useMutationState({
|
|
1671
|
+
filters: { mutationKey: ['upload'], status: 'pending' },
|
|
1672
|
+
select: (mutation) => mutation.state.variables,
|
|
1673
|
+
})
|
|
1674
|
+
|
|
1675
|
+
return (
|
|
1676
|
+
<div>
|
|
1677
|
+
<h3>正在上传 ({uploads.length})</h3>
|
|
1678
|
+
<ul>
|
|
1679
|
+
{uploads.map((file, i) => (
|
|
1680
|
+
<li key={i}>{file.name} 正在传输...</li>
|
|
1681
|
+
))}
|
|
1682
|
+
</ul>
|
|
1683
|
+
</div>
|
|
1684
|
+
)
|
|
1685
|
+
}
|
|
1686
|
+
```
|
|
1687
|
+
|
|
1688
|
+
---
|
|
1689
|
+
|
|
1690
|
+
## 10. 第八步:批量查询与仪表盘
|
|
1617
1691
|
|
|
1618
1692
|
|
|
1619
1693
|
在仪表盘、数据概览等场景中,我们经常需要同时发起多个查询。本库提供了强大的批量查询功能,包括统计信息、批量操作和错误聚合。
|
|
@@ -2266,74 +2340,49 @@ function ProductBrowser() {
|
|
|
2266
2340
|
|
|
2267
2341
|
---
|
|
2268
2342
|
|
|
2269
|
-
##
|
|
2343
|
+
## 12. 第十步:Suspense 模式与 SSR
|
|
2270
2344
|
|
|
2345
|
+
React Suspense 提供了声明式的加载处理。本库不仅支持增强的 Suspense Hooks,还提供了完善的 SSR 水合支持。
|
|
2271
2346
|
|
|
2272
|
-
|
|
2347
|
+
### 12.1 基础 Suspense 查询
|
|
2273
2348
|
|
|
2274
|
-
|
|
2349
|
+
`useEnhancedSuspenseQuery` 保证 `data` 始终存在,省去非空判断:
|
|
2275
2350
|
|
|
2276
|
-
**传统模式:**
|
|
2277
2351
|
```tsx
|
|
2278
|
-
|
|
2279
|
-
const { data, isLoading, isError, error } = useQuery({
|
|
2280
|
-
queryKey: ['user', userId],
|
|
2281
|
-
queryFn: () => fetchUser(userId),
|
|
2282
|
-
})
|
|
2283
|
-
|
|
2284
|
-
if (isLoading) return <Loading />
|
|
2285
|
-
if (isError) return <Error message={error.message} />
|
|
2286
|
-
|
|
2287
|
-
return <div>{data.name}</div>
|
|
2288
|
-
}
|
|
2289
|
-
```
|
|
2352
|
+
import { useEnhancedSuspenseQuery } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
2290
2353
|
|
|
2291
|
-
**Suspense 模式:**
|
|
2292
|
-
```tsx
|
|
2293
2354
|
function UserProfile({ userId }) {
|
|
2294
|
-
|
|
2295
|
-
const { data } = useSuspenseQuery({
|
|
2355
|
+
const { data } = useEnhancedSuspenseQuery({
|
|
2296
2356
|
queryKey: ['user', userId],
|
|
2297
2357
|
queryFn: () => fetchUser(userId),
|
|
2298
2358
|
})
|
|
2299
2359
|
|
|
2300
2360
|
return <div>{data.name}</div>
|
|
2301
2361
|
}
|
|
2302
|
-
|
|
2303
|
-
// 在父组件处理 loading 和 error
|
|
2304
|
-
function UserPage({ userId }) {
|
|
2305
|
-
return (
|
|
2306
|
-
<Suspense fallback={<Loading />}>
|
|
2307
|
-
<ErrorBoundary fallback={<Error />}>
|
|
2308
|
-
<UserProfile userId={userId} />
|
|
2309
|
-
</ErrorBoundary>
|
|
2310
|
-
</Suspense>
|
|
2311
|
-
)
|
|
2312
|
-
}
|
|
2313
2362
|
```
|
|
2314
2363
|
|
|
2315
|
-
###
|
|
2364
|
+
### 12.2 SSR 水合支持
|
|
2365
|
+
|
|
2366
|
+
在 Next.js (App Router) 或其他 SSR 框架中,使用 `HydrationBoundary` 进行注水:
|
|
2316
2367
|
|
|
2317
2368
|
```tsx
|
|
2318
|
-
|
|
2369
|
+
// Server Component
|
|
2370
|
+
import { dehydrate, QueryClient } from '@qiaopeng/tanstack-query-plus'
|
|
2371
|
+
import { HydrationBoundary } from '@qiaopeng/tanstack-query-plus/components'
|
|
2319
2372
|
|
|
2320
|
-
function
|
|
2321
|
-
const
|
|
2322
|
-
|
|
2323
|
-
queryFn: () => fetchUser(userId),
|
|
2324
|
-
})
|
|
2373
|
+
export default async function Page() {
|
|
2374
|
+
const queryClient = new QueryClient()
|
|
2375
|
+
await queryClient.prefetchQuery({ queryKey: ['posts'], queryFn: fetchPosts })
|
|
2325
2376
|
|
|
2326
|
-
// data 一定存在,TypeScript 类型也是非空的
|
|
2327
2377
|
return (
|
|
2328
|
-
<
|
|
2329
|
-
<
|
|
2330
|
-
|
|
2331
|
-
</div>
|
|
2378
|
+
<HydrationBoundary state={dehydrate(queryClient)}>
|
|
2379
|
+
<PostsList />
|
|
2380
|
+
</HydrationBoundary>
|
|
2332
2381
|
)
|
|
2333
2382
|
}
|
|
2334
2383
|
```
|
|
2335
2384
|
|
|
2336
|
-
###
|
|
2385
|
+
### 12.3 使用 SuspenseWrapper 组件
|
|
2337
2386
|
|
|
2338
2387
|
本库提供了 `SuspenseWrapper` 和 `QuerySuspenseWrapper` 组件,它们组合了 Suspense 和 ErrorBoundary:
|
|
2339
2388
|
|
|
@@ -2710,7 +2759,8 @@ console.log({
|
|
|
2710
2759
|
对于需要在离线时也能操作的应用,可以使用离线队列管理器:
|
|
2711
2760
|
|
|
2712
2761
|
```tsx
|
|
2713
|
-
import { createOfflineQueueManager, mutationRegistry } from '@qiaopeng/tanstack-query-plus/features'
|
|
2762
|
+
import { createOfflineQueueManager, isOnline, mutationRegistry, serializeMutationKey } from '@qiaopeng/tanstack-query-plus/features'
|
|
2763
|
+
import { MutationOperationType } from '@qiaopeng/tanstack-query-plus/types'
|
|
2714
2764
|
|
|
2715
2765
|
// 创建队列管理器
|
|
2716
2766
|
const queueManager = createOfflineQueueManager({
|
|
@@ -2723,15 +2773,17 @@ const queueManager = createOfflineQueueManager({
|
|
|
2723
2773
|
|
|
2724
2774
|
// 注册 mutation 函数(用于恢复队列时执行)
|
|
2725
2775
|
// 注册函数签名为 () => Promise<unknown>,如需变量请使用闭包或在入队项的 mutationFn 捕获
|
|
2726
|
-
mutationRegistry.register('updateUser', () => updateUserAPI(savedUserData))
|
|
2727
|
-
mutationRegistry.register('createPost', () => createPostAPI(savedPostData))
|
|
2776
|
+
mutationRegistry.register(serializeMutationKey(['updateUser']), () => updateUserAPI(savedUserData))
|
|
2777
|
+
mutationRegistry.register(serializeMutationKey(['createPost']), () => createPostAPI(savedPostData))
|
|
2728
2778
|
|
|
2729
2779
|
// 添加操作到队列
|
|
2730
2780
|
async function handleUpdateUser(userData) {
|
|
2731
2781
|
if (!isOnline()) {
|
|
2732
2782
|
// 离线时添加到队列
|
|
2733
2783
|
await queueManager.add({
|
|
2784
|
+
type: MutationOperationType.UPDATE,
|
|
2734
2785
|
mutationKey: ['updateUser'],
|
|
2786
|
+
variables: userData,
|
|
2735
2787
|
mutationFn: () => updateUserAPI(userData),
|
|
2736
2788
|
priority: 1, // 优先级(数字越大越优先)
|
|
2737
2789
|
})
|
|
@@ -2772,6 +2824,7 @@ queueManager.destroy()
|
|
|
2772
2824
|
```tsx
|
|
2773
2825
|
import { useState, useEffect } from 'react'
|
|
2774
2826
|
import { createOfflineQueueManager } from '@qiaopeng/tanstack-query-plus/features'
|
|
2827
|
+
import { MutationOperationType } from '@qiaopeng/tanstack-query-plus/types'
|
|
2775
2828
|
import { useEnhancedQuery } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
2776
2829
|
import { useQueryClient, usePersistenceStatus } from '@qiaopeng/tanstack-query-plus'
|
|
2777
2830
|
|
|
@@ -2817,7 +2870,9 @@ function TodoApp() {
|
|
|
2817
2870
|
if (!networkStatus) {
|
|
2818
2871
|
// 离线:添加到队列
|
|
2819
2872
|
await offlineQueue.add({
|
|
2873
|
+
type: MutationOperationType.CREATE,
|
|
2820
2874
|
mutationKey: ['addTodo'],
|
|
2875
|
+
variables: todoData,
|
|
2821
2876
|
mutationFn: () => api.createTodo(todoData),
|
|
2822
2877
|
priority: 1,
|
|
2823
2878
|
})
|
|
@@ -3675,7 +3730,13 @@ function UserProfile({ userId }) {
|
|
|
3675
3730
|
import { QueryClient } from '@qiaopeng/tanstack-query-plus'
|
|
3676
3731
|
import { getConfigByEnvironment, ensureBestPractices } from '@qiaopeng/tanstack-query-plus/core'
|
|
3677
3732
|
|
|
3678
|
-
const
|
|
3733
|
+
const env =
|
|
3734
|
+
process.env.NODE_ENV === 'production'
|
|
3735
|
+
? 'production'
|
|
3736
|
+
: process.env.NODE_ENV === 'test'
|
|
3737
|
+
? 'test'
|
|
3738
|
+
: 'development'
|
|
3739
|
+
const baseConfig = getConfigByEnvironment(env)
|
|
3679
3740
|
|
|
3680
3741
|
// 确保配置符合最佳实践
|
|
3681
3742
|
const config = ensureBestPractices({
|
|
@@ -3751,12 +3812,12 @@ function useUser(userId: string): EnhancedQueryResult<User, ApiError> {
|
|
|
3751
3812
|
}
|
|
3752
3813
|
|
|
3753
3814
|
// 类型安全的 mutation
|
|
3754
|
-
function useUpdateUser() {
|
|
3815
|
+
function useUpdateUser(userId: string) {
|
|
3755
3816
|
return useMutation<User, ApiError, Partial<User>>({
|
|
3756
|
-
mutationFn: (
|
|
3817
|
+
mutationFn: (patch) => updateUser(userId, patch),
|
|
3757
3818
|
optimistic: {
|
|
3758
|
-
queryKey: ['user',
|
|
3759
|
-
updater: (old,
|
|
3819
|
+
queryKey: ['user', userId],
|
|
3820
|
+
updater: (old, patch) => (old ? ({ ...old, ...patch }) : old),
|
|
3760
3821
|
},
|
|
3761
3822
|
})
|
|
3762
3823
|
}
|
|
@@ -3886,32 +3947,6 @@ useEnhancedQuery({
|
|
|
3886
3947
|
3. **处理认证过期**:在全局错误处理中处理 401 错误
|
|
3887
3948
|
4. **清理敏感缓存**:用户登出时清除缓存
|
|
3888
3949
|
|
|
3889
|
-
---
|
|
3890
|
-
|
|
3891
|
-
## 总结
|
|
3892
|
-
|
|
3893
|
-
恭喜你完成了本教程!现在你已经掌握了 `@qiaopeng/tanstack-query-plus` 的所有核心功能:
|
|
3894
|
-
|
|
3895
|
-
1. ✅ 配置 Provider 和最佳实践
|
|
3896
|
-
2. ✅ 基础查询和增强查询
|
|
3897
|
-
3. ✅ Query Key 管理
|
|
3898
|
-
4. ✅ 数据变更和乐观更新
|
|
3899
|
-
5. ✅ 无限滚动和分页
|
|
3900
|
-
6. ✅ 批量查询和仪表盘
|
|
3901
|
-
7. ✅ 智能预取策略
|
|
3902
|
-
8. ✅ Suspense 模式
|
|
3903
|
-
9. ✅ 离线支持和持久化
|
|
3904
|
-
10. ✅ 数据防护与安全
|
|
3905
|
-
11. ✅ 焦点管理
|
|
3906
|
-
12. ✅ 工具函数和选择器
|
|
3907
|
-
|
|
3908
|
-
### 下一步
|
|
3909
|
-
|
|
3910
|
-
- 查看 [GitHub 仓库](https://github.com/qiaopengg/qiaopeng-tanstack-query-plus) 获取最新更新
|
|
3911
|
-
- 阅读 [TanStack Query 官方文档](https://tanstack.com/query/latest) 了解更多底层概念
|
|
3912
|
-
- 在 [Issues](https://github.com/qiaopengg/qiaopeng-tanstack-query-plus/issues) 中提问或反馈
|
|
3913
|
-
|
|
3914
|
-
祝你编码愉快!🚀
|
|
3915
3950
|
### 16.9 类型与错误处理规范
|
|
3916
3951
|
|
|
3917
3952
|
- 明确类型参数:在增强 hooks 中显式标注 `TData` 与 `TError`,避免 `any` 漏出
|
|
@@ -3979,13 +4014,13 @@ useEnhancedQuery({
|
|
|
3979
4014
|
- 数据防护:`useDataGuardQueryConfig`、`useDataGuardMutation`
|
|
3980
4015
|
|
|
3981
4016
|
- `@qiaopeng/tanstack-query-plus/features`
|
|
3982
|
-
- 离线:`setupOnlineManager`、`isOnline`、`createOfflineQueueManager`、`OfflineQueueManager`、`mutationRegistry`、`subscribeToOnlineStatus`
|
|
4017
|
+
- 离线:`setupOnlineManager`、`isOnline`、`createOfflineQueueManager`、`OfflineQueueManager`、`mutationRegistry`、`serializeMutationKey`、`subscribeToOnlineStatus`
|
|
3983
4018
|
- 持久化:`createPersistOptions`、`createPersister`、`clearCache`、`clearExpiredCache`、`checkStorageSize`、`getStorageStats`、`migrateToIndexedDB`
|
|
3984
4019
|
|
|
3985
4020
|
- `@qiaopeng/tanstack-query-plus/components`
|
|
3986
4021
|
- Loading:`DefaultLoadingFallback`、`FullScreenLoading`、`ListSkeletonFallback`、`PageSkeletonFallback`、`SmallLoadingIndicator`、`TextSkeletonFallback`
|
|
3987
4022
|
- 错误边界:`QueryErrorBoundary`
|
|
3988
|
-
- Suspense:`SuspenseWrapper`、`QuerySuspenseWrapper`
|
|
4023
|
+
- Suspense & SSR:`SuspenseWrapper`、`QuerySuspenseWrapper`、`HydrationBoundary`
|
|
3989
4024
|
|
|
3990
4025
|
- `@qiaopeng/tanstack-query-plus/utils`
|
|
3991
4026
|
- 选择器:`selectById`、`selectFields`、`compose` 等
|
|
@@ -3999,3 +4034,28 @@ useEnhancedQuery({
|
|
|
3999
4034
|
- 原生 API 再导出:`useQuery`、`useMutation`、`useInfiniteQuery`、`useSuspenseQuery` 等(src/react-query/index.ts:1)
|
|
4000
4035
|
|
|
4001
4036
|
提示:完整导出列表可在 `package.json:33` 的 `exports` 字段中查看;顶层入口再导出常用原生 API,子路径按模块分层导出,便于 tree-shaking。
|
|
4037
|
+
|
|
4038
|
+
---
|
|
4039
|
+
|
|
4040
|
+
## 总结
|
|
4041
|
+
|
|
4042
|
+
你已经覆盖了 `@qiaopeng/tanstack-query-plus` 的核心能力:
|
|
4043
|
+
|
|
4044
|
+
1. ✅ 配置 Provider 和最佳实践
|
|
4045
|
+
2. ✅ 基础查询和增强查询
|
|
4046
|
+
3. ✅ Query Key 管理
|
|
4047
|
+
4. ✅ 数据变更和乐观更新
|
|
4048
|
+
5. ✅ 无限滚动和分页
|
|
4049
|
+
6. ✅ 批量查询和仪表盘
|
|
4050
|
+
7. ✅ 智能预取策略
|
|
4051
|
+
8. ✅ Suspense 模式
|
|
4052
|
+
9. ✅ 离线支持和持久化
|
|
4053
|
+
10. ✅ 数据防护与安全
|
|
4054
|
+
11. ✅ 焦点管理
|
|
4055
|
+
12. ✅ 工具函数和选择器
|
|
4056
|
+
|
|
4057
|
+
### 下一步
|
|
4058
|
+
|
|
4059
|
+
- 查看 [GitHub 仓库](https://github.com/qiaopengg/qiaopeng-tanstack-query-plus) 获取最新更新
|
|
4060
|
+
- 阅读 [TanStack Query 官方文档](https://tanstack.com/query/latest) 了解更多底层概念
|
|
4061
|
+
- 在 [Issues](https://github.com/qiaopengg/qiaopeng-tanstack-query-plus/issues) 中提问或反馈
|
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
import type { PersistedClient, Persister } from "@tanstack/react-query-persist-client";
|
|
3
|
-
export declare enum StorageType {
|
|
1
|
+
declare enum StorageType {
|
|
4
2
|
LOCAL = "localStorage",
|
|
5
3
|
SESSION = "sessionStorage",
|
|
6
4
|
INDEXED_DB = "indexedDB",
|
|
7
5
|
CUSTOM = "custom"
|
|
8
6
|
}
|
|
9
|
-
|
|
7
|
+
declare enum ConnectionQuality {
|
|
10
8
|
SLOW = "slow",
|
|
11
9
|
FAST = "fast",
|
|
12
10
|
UNKNOWN = "unknown"
|
|
13
11
|
}
|
|
14
|
-
|
|
12
|
+
declare enum CachePriority {
|
|
15
13
|
HIGH = "high",
|
|
16
14
|
MEDIUM = "medium",
|
|
17
15
|
LOW = "low"
|
|
18
16
|
}
|
|
19
|
-
|
|
17
|
+
declare enum PersistenceStrategyType {
|
|
20
18
|
AGGRESSIVE = "aggressive",
|
|
21
19
|
CONSERVATIVE = "conservative",
|
|
22
20
|
SELECTIVE = "selective",
|
|
23
21
|
CUSTOM = "custom"
|
|
24
22
|
}
|
|
25
|
-
|
|
23
|
+
interface OperationResult<T = unknown> {
|
|
26
24
|
success: boolean;
|
|
27
25
|
data?: T;
|
|
28
26
|
error?: Error;
|
|
29
27
|
duration?: number;
|
|
30
28
|
metadata?: Record<string, unknown>;
|
|
31
29
|
}
|
|
32
|
-
|
|
30
|
+
interface NetworkInformation {
|
|
33
31
|
effectiveType?: "4g" | "3g" | "2g" | "slow-2g";
|
|
34
32
|
saveData?: boolean;
|
|
35
33
|
downlink?: number;
|
|
36
34
|
rtt?: number;
|
|
37
35
|
}
|
|
38
|
-
|
|
36
|
+
interface NetworkStatus {
|
|
39
37
|
isOnline: boolean;
|
|
40
38
|
isOffline: boolean;
|
|
41
39
|
connectionQuality: ConnectionQuality;
|
|
@@ -43,12 +41,12 @@ export interface NetworkStatus {
|
|
|
43
41
|
lastOnlineAt?: Date;
|
|
44
42
|
lastOfflineAt?: Date;
|
|
45
43
|
}
|
|
46
|
-
|
|
44
|
+
type DeepReadonly<T> = {
|
|
47
45
|
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
|
|
48
46
|
};
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
48
|
+
type Required<T, K extends keyof T> = T & {
|
|
51
49
|
[P in K]-?: T[P];
|
|
52
50
|
};
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
|
|
52
|
+
export { ConnectionQuality as C, type DeepReadonly as D, type NetworkInformation as N, type OperationResult as O, PersistenceStrategyType as P, type Required as R, StorageType as S, CachePriority as a, type NetworkStatus as b, type Optional as c };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
declare enum StorageType {
|
|
2
|
+
LOCAL = "localStorage",
|
|
3
|
+
SESSION = "sessionStorage",
|
|
4
|
+
INDEXED_DB = "indexedDB",
|
|
5
|
+
CUSTOM = "custom"
|
|
6
|
+
}
|
|
7
|
+
declare enum ConnectionQuality {
|
|
8
|
+
SLOW = "slow",
|
|
9
|
+
FAST = "fast",
|
|
10
|
+
UNKNOWN = "unknown"
|
|
11
|
+
}
|
|
12
|
+
declare enum CachePriority {
|
|
13
|
+
HIGH = "high",
|
|
14
|
+
MEDIUM = "medium",
|
|
15
|
+
LOW = "low"
|
|
16
|
+
}
|
|
17
|
+
declare enum PersistenceStrategyType {
|
|
18
|
+
AGGRESSIVE = "aggressive",
|
|
19
|
+
CONSERVATIVE = "conservative",
|
|
20
|
+
SELECTIVE = "selective",
|
|
21
|
+
CUSTOM = "custom"
|
|
22
|
+
}
|
|
23
|
+
interface OperationResult<T = unknown> {
|
|
24
|
+
success: boolean;
|
|
25
|
+
data?: T;
|
|
26
|
+
error?: Error;
|
|
27
|
+
duration?: number;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
interface NetworkInformation {
|
|
31
|
+
effectiveType?: "4g" | "3g" | "2g" | "slow-2g";
|
|
32
|
+
saveData?: boolean;
|
|
33
|
+
downlink?: number;
|
|
34
|
+
rtt?: number;
|
|
35
|
+
}
|
|
36
|
+
interface NetworkStatus {
|
|
37
|
+
isOnline: boolean;
|
|
38
|
+
isOffline: boolean;
|
|
39
|
+
connectionQuality: ConnectionQuality;
|
|
40
|
+
connection?: NetworkInformation;
|
|
41
|
+
lastOnlineAt?: Date;
|
|
42
|
+
lastOfflineAt?: Date;
|
|
43
|
+
}
|
|
44
|
+
type DeepReadonly<T> = {
|
|
45
|
+
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
|
|
46
|
+
};
|
|
47
|
+
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
48
|
+
type Required<T, K extends keyof T> = T & {
|
|
49
|
+
[P in K]-?: T[P];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export { ConnectionQuality as C, type DeepReadonly as D, type NetworkInformation as N, type OperationResult as O, PersistenceStrategyType as P, type Required as R, StorageType as S, CachePriority as a, type NetworkStatus as b, type Optional as c };
|