@qiaopeng/tanstack-query-plus 0.5.7 → 0.5.8
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 +66 -6
- package/dist/core/config.d.ts +37 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +93 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -291,18 +291,30 @@ function App() {
|
|
|
291
291
|
queries: {
|
|
292
292
|
staleTime: 30000,
|
|
293
293
|
gcTime: 600000,
|
|
294
|
-
retry: defaultQueryRetryStrategy,
|
|
294
|
+
retry: defaultQueryRetryStrategy, // 智能重试:4XX不重试,5XX最多1次
|
|
295
295
|
retryDelay: exponentialBackoff,
|
|
296
296
|
refetchOnWindowFocus: true,
|
|
297
297
|
refetchOnReconnect: true,
|
|
298
298
|
},
|
|
299
299
|
mutations: {
|
|
300
|
-
retry: 0,
|
|
300
|
+
retry: 0, // Mutation 默认不重试
|
|
301
301
|
gcTime: 600000,
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
+
**重试策略说明:**
|
|
307
|
+
|
|
308
|
+
- **Query 重试**(`defaultQueryRetryStrategy`):
|
|
309
|
+
- 4XX 客户端错误:不重试(客户端问题,重试无意义)
|
|
310
|
+
- 5XX 服务端错误:最多重试 1 次(避免过度重试)
|
|
311
|
+
- 网络错误:最多重试 2 次
|
|
312
|
+
|
|
313
|
+
- **Mutation 重试**(`defaultMutationRetryStrategy`):
|
|
314
|
+
- 4XX 客户端错误:不重试
|
|
315
|
+
- 5XX 服务端错误:不重试(避免重复操作)
|
|
316
|
+
- 网络错误:最多重试 1 次
|
|
317
|
+
|
|
306
318
|
这些值是经过实践验证的最佳实践,适合大多数应用场景。
|
|
307
319
|
|
|
308
320
|
### 3.4 根据环境选择配置
|
|
@@ -322,10 +334,58 @@ const queryClient = new QueryClient({ defaultOptions: config })
|
|
|
322
334
|
| 配置项 | development | production | test |
|
|
323
335
|
|--------|-------------|------------|------|
|
|
324
336
|
| staleTime | 0 | 10 分钟 | 0 |
|
|
325
|
-
| retry |
|
|
337
|
+
| retry (Query) | 智能重试* | 智能重试* | 0 |
|
|
338
|
+
| retry (Mutation) | 0 | 0 | 0 |
|
|
326
339
|
| refetchOnWindowFocus | true | true | false |
|
|
327
340
|
|
|
328
|
-
|
|
341
|
+
*智能重试:4XX 不重试,5XX 最多 1 次,网络错误最多 2 次
|
|
342
|
+
|
|
343
|
+
### 3.5 自定义重试策略
|
|
344
|
+
|
|
345
|
+
如果默认的重试策略不满足你的需求,可以使用 `createSafeRetryStrategy` 和 `createErrorSafeConfig` 来自定义:
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
import {
|
|
349
|
+
createSafeRetryStrategy,
|
|
350
|
+
createErrorSafeConfig
|
|
351
|
+
} from '@qiaopeng/tanstack-query-plus/core'
|
|
352
|
+
|
|
353
|
+
// 方式一:创建自定义重试策略
|
|
354
|
+
const customRetry = createSafeRetryStrategy(
|
|
355
|
+
0, // 4XX 错误重试次数
|
|
356
|
+
1, // 5XX 错误重试次数
|
|
357
|
+
2 // 其他错误重试次数
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
const queryClient = new QueryClient({
|
|
361
|
+
defaultOptions: {
|
|
362
|
+
queries: {
|
|
363
|
+
retry: customRetry,
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// 方式二:使用错误安全配置(推荐)
|
|
369
|
+
const errorSafeConfig = createErrorSafeConfig({
|
|
370
|
+
maxRetries4xx: 0, // 4XX 不重试
|
|
371
|
+
maxRetries5xx: 0, // 5XX 不重试(严格模式)
|
|
372
|
+
maxRetriesOther: 1, // 网络错误最多 1 次
|
|
373
|
+
disableFocus: false, // 是否禁用窗口聚焦时 refetch
|
|
374
|
+
disableReconnect: false // 是否禁用重连时 refetch
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
const queryClient = new QueryClient({
|
|
378
|
+
defaultOptions: errorSafeConfig
|
|
379
|
+
})
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**使用场景:**
|
|
383
|
+
|
|
384
|
+
1. **严格模式**:完全禁用 4XX/5XX 重试,避免不必要的请求
|
|
385
|
+
2. **宽松模式**:增加重试次数,适合网络不稳定的环境
|
|
386
|
+
3. **自定义场景**:根据业务需求精确控制重试行为
|
|
387
|
+
|
|
388
|
+
### 3.6 添加 DevTools(开发环境)
|
|
329
389
|
|
|
330
390
|
在开发环境中,强烈建议添加 DevTools 来调试查询状态:
|
|
331
391
|
|
|
@@ -3902,8 +3962,8 @@ useEnhancedQuery({
|
|
|
3902
3962
|
- `QueryClient`、`QueryClientProvider`、`useQueryClient`、`skipToken`、`useIsMutating`(直接再导出原生 API)
|
|
3903
3963
|
|
|
3904
3964
|
- `@qiaopeng/tanstack-query-plus/core`
|
|
3905
|
-
- 配置:`GLOBAL_QUERY_CONFIG`、`createCustomConfig`、`DEFAULT_STALE_TIME`、`DEFAULT_GC_TIME`
|
|
3906
|
-
- 重试:`defaultQueryRetryStrategy`、`defaultMutationRetryStrategy`、`exponentialBackoff`
|
|
3965
|
+
- 配置:`GLOBAL_QUERY_CONFIG`、`createCustomConfig`、`createErrorSafeConfig`、`DEFAULT_STALE_TIME`、`DEFAULT_GC_TIME`
|
|
3966
|
+
- 重试:`defaultQueryRetryStrategy`、`defaultMutationRetryStrategy`、`createSafeRetryStrategy`、`exponentialBackoff`
|
|
3907
3967
|
- 环境:`isDev`、`isProd`、`isTest`
|
|
3908
3968
|
- DevTools:`ReactQueryDevtools`、`isDevToolsEnabled`、`createDevToolsConfig`(src/core/devtools.ts:28)
|
|
3909
3969
|
- 焦点管理:`focusManager`、`getSmartFocusManager`、`pauseFocusManager`、`resumeFocusManager`
|
package/dist/core/config.d.ts
CHANGED
|
@@ -11,7 +11,19 @@ export declare const TIME_CONSTANTS: {
|
|
|
11
11
|
};
|
|
12
12
|
export declare const DEFAULT_STALE_TIME: number;
|
|
13
13
|
export declare const DEFAULT_GC_TIME: number;
|
|
14
|
+
/**
|
|
15
|
+
* 默认 Query 重试策略
|
|
16
|
+
* - 4XX 客户端错误:不重试(客户端问题,重试无意义)
|
|
17
|
+
* - 5XX 服务端错误:最多重试 1 次(避免过度重试)
|
|
18
|
+
* - 其他错误(网络等):最多重试 2 次
|
|
19
|
+
*/
|
|
14
20
|
export declare function defaultQueryRetryStrategy(failureCount: number, error: unknown): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 默认 Mutation 重试策略
|
|
23
|
+
* - 4XX 客户端错误:不重试
|
|
24
|
+
* - 5XX 服务端错误:不重试(Mutation 更谨慎,避免重复操作)
|
|
25
|
+
* - 其他错误:最多重试 1 次
|
|
26
|
+
*/
|
|
15
27
|
export declare function defaultMutationRetryStrategy(failureCount: number, error: unknown): boolean;
|
|
16
28
|
export declare function exponentialBackoff(attemptIndex: number): number;
|
|
17
29
|
export declare const DEFAULT_QUERY_CONFIG: DefaultOptions["queries"];
|
|
@@ -33,9 +45,32 @@ export declare function validateConfig(config: DefaultOptions): {
|
|
|
33
45
|
warnings: string[];
|
|
34
46
|
};
|
|
35
47
|
export declare function ensureBestPractices(config: DefaultOptions): DefaultOptions;
|
|
36
|
-
|
|
48
|
+
/**
|
|
49
|
+
* 创建安全的重试策略
|
|
50
|
+
* @param maxRetries4xx - 4XX 错误最大重试次数(默认 0,不重试)
|
|
51
|
+
* @param maxRetries5xx - 5XX 错误最大重试次数(默认 0,不重试)
|
|
52
|
+
* @param maxRetriesOther - 其他错误最大重试次数(默认 1)
|
|
53
|
+
*/
|
|
54
|
+
export declare function createSafeRetryStrategy(maxRetries4xx?: number, maxRetries5xx?: number, maxRetriesOther?: number): (failureCount: number, error: unknown) => boolean;
|
|
55
|
+
/**
|
|
56
|
+
* 创建错误安全配置
|
|
57
|
+
* 适用于需要严格控制重试和 refetch 行为的场景
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // 完全禁用重试
|
|
62
|
+
* const config = createErrorSafeConfig({
|
|
63
|
+
* maxRetries4xx: 0,
|
|
64
|
+
* maxRetries5xx: 0,
|
|
65
|
+
* maxRetriesOther: 0,
|
|
66
|
+
* disableFocus: true,
|
|
67
|
+
* disableReconnect: true
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
37
71
|
export declare function createErrorSafeConfig(options?: {
|
|
38
|
-
|
|
72
|
+
maxRetries4xx?: number;
|
|
73
|
+
maxRetries5xx?: number;
|
|
39
74
|
maxRetriesOther?: number;
|
|
40
75
|
disableFocus?: boolean;
|
|
41
76
|
disableReconnect?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,eAAO,MAAM,cAAc;;;;;;;;;CASjB,CAAC;AAEX,eAAO,MAAM,kBAAkB,QAAgC,CAAC;AAChE,eAAO,MAAM,eAAe,QAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,eAAO,MAAM,cAAc;;;;;;;;;CASjB,CAAC;AAEX,eAAO,MAAM,kBAAkB,QAAgC,CAAC;AAChE,eAAO,MAAM,eAAe,QAA6B,CAAC;AAkC1D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAevF;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAe1F;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,eAAO,MAAM,oBAAoB,EAAE,cAAc,CAAC,SAAS,CAQ1D,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,WAAW,CAI/D,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,cAGjC,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,cAAc,CAAC,WAAW,CAInE,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAYhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAY/B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAa/B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,cAW7B,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,YAAY,GAAG,MAAM,GAAG,cAAc,CAwBjG;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAWrF;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAQxG;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA2B/F;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAkC1E;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,aAAa,GAAE,MAAU,EACzB,aAAa,GAAE,MAAU,EACzB,eAAe,GAAE,MAAU,IAEnB,cAAc,MAAM,EAAE,OAAO,OAAO,KAAG,OAAO,CAgBvD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,MAAM,GAAG,KAAK,CAAC,CAAC;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAChC,GAAG,cAAc,CAsCtB"}
|
package/dist/core/config.js
CHANGED
|
@@ -11,19 +11,65 @@ export const TIME_CONSTANTS = {
|
|
|
11
11
|
};
|
|
12
12
|
export const DEFAULT_STALE_TIME = TIME_CONSTANTS.THIRTY_SECONDS;
|
|
13
13
|
export const DEFAULT_GC_TIME = TIME_CONSTANTS.TEN_MINUTES;
|
|
14
|
+
/**
|
|
15
|
+
* 从错误对象中提取 HTTP 状态码
|
|
16
|
+
* 兼容多种错误对象结构:axios、fetch、自定义等
|
|
17
|
+
*/
|
|
18
|
+
function extractHttpStatus(error) {
|
|
19
|
+
if (!error || typeof error !== "object")
|
|
20
|
+
return undefined;
|
|
21
|
+
const err = error;
|
|
22
|
+
// 直接在错误对象上的 status
|
|
23
|
+
if (typeof err.status === "number")
|
|
24
|
+
return err.status;
|
|
25
|
+
if (typeof err.statusCode === "number")
|
|
26
|
+
return err.statusCode;
|
|
27
|
+
// 在 response 对象中的 status (axios 等)
|
|
28
|
+
if (err.response) {
|
|
29
|
+
if (typeof err.response.status === "number")
|
|
30
|
+
return err.response.status;
|
|
31
|
+
if (typeof err.response.statusCode === "number")
|
|
32
|
+
return err.response.statusCode;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 默认 Query 重试策略
|
|
38
|
+
* - 4XX 客户端错误:不重试(客户端问题,重试无意义)
|
|
39
|
+
* - 5XX 服务端错误:最多重试 1 次(避免过度重试)
|
|
40
|
+
* - 其他错误(网络等):最多重试 2 次
|
|
41
|
+
*/
|
|
14
42
|
export function defaultQueryRetryStrategy(failureCount, error) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
43
|
+
const status = extractHttpStatus(error);
|
|
44
|
+
// 4XX 客户端错误:不重试
|
|
45
|
+
if (status && status >= 400 && status < 500) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
// 5XX 服务端错误:最多重试 1 次
|
|
49
|
+
if (status && status >= 500 && status < 600) {
|
|
50
|
+
return failureCount < 1;
|
|
51
|
+
}
|
|
52
|
+
// 其他错误(网络错误等):最多重试 2 次
|
|
18
53
|
return failureCount < 2;
|
|
19
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* 默认 Mutation 重试策略
|
|
57
|
+
* - 4XX 客户端错误:不重试
|
|
58
|
+
* - 5XX 服务端错误:不重试(Mutation 更谨慎,避免重复操作)
|
|
59
|
+
* - 其他错误:最多重试 1 次
|
|
60
|
+
*/
|
|
20
61
|
export function defaultMutationRetryStrategy(failureCount, error) {
|
|
21
|
-
const
|
|
22
|
-
|
|
62
|
+
const status = extractHttpStatus(error);
|
|
63
|
+
// 4XX 客户端错误:不重试
|
|
64
|
+
if (status && status >= 400 && status < 500) {
|
|
23
65
|
return false;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
66
|
+
}
|
|
67
|
+
// 5XX 服务端错误:不重试(Mutation 避免重复操作)
|
|
68
|
+
if (status && status >= 500 && status < 600) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
// 其他错误(网络错误等):最多重试 1 次
|
|
72
|
+
return failureCount < 1;
|
|
27
73
|
}
|
|
28
74
|
export function exponentialBackoff(attemptIndex) {
|
|
29
75
|
return Math.min(1000 * 2 ** attemptIndex, 30000);
|
|
@@ -209,21 +255,48 @@ export function ensureBestPractices(config) {
|
|
|
209
255
|
}
|
|
210
256
|
return result;
|
|
211
257
|
}
|
|
212
|
-
|
|
258
|
+
/**
|
|
259
|
+
* 创建安全的重试策略
|
|
260
|
+
* @param maxRetries4xx - 4XX 错误最大重试次数(默认 0,不重试)
|
|
261
|
+
* @param maxRetries5xx - 5XX 错误最大重试次数(默认 0,不重试)
|
|
262
|
+
* @param maxRetriesOther - 其他错误最大重试次数(默认 1)
|
|
263
|
+
*/
|
|
264
|
+
export function createSafeRetryStrategy(maxRetries4xx = 0, maxRetries5xx = 0, maxRetriesOther = 1) {
|
|
213
265
|
return (failureCount, error) => {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
266
|
+
const status = extractHttpStatus(error);
|
|
267
|
+
// 4XX 客户端错误
|
|
268
|
+
if (status && status >= 400 && status < 500) {
|
|
269
|
+
return failureCount < maxRetries4xx;
|
|
270
|
+
}
|
|
271
|
+
// 5XX 服务端错误
|
|
272
|
+
if (status && status >= 500 && status < 600) {
|
|
273
|
+
return failureCount < maxRetries5xx;
|
|
274
|
+
}
|
|
275
|
+
// 其他错误(网络等)
|
|
219
276
|
return failureCount < maxRetriesOther;
|
|
220
277
|
};
|
|
221
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* 创建错误安全配置
|
|
281
|
+
* 适用于需要严格控制重试和 refetch 行为的场景
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* ```typescript
|
|
285
|
+
* // 完全禁用重试
|
|
286
|
+
* const config = createErrorSafeConfig({
|
|
287
|
+
* maxRetries4xx: 0,
|
|
288
|
+
* maxRetries5xx: 0,
|
|
289
|
+
* maxRetriesOther: 0,
|
|
290
|
+
* disableFocus: true,
|
|
291
|
+
* disableReconnect: true
|
|
292
|
+
* });
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
222
295
|
export function createErrorSafeConfig(options = {}) {
|
|
223
|
-
const {
|
|
296
|
+
const { maxRetries4xx = 0, maxRetries5xx = 0, maxRetriesOther = 1, disableFocus = false, disableReconnect = false, conditionalRefetchInterval, overrides } = options;
|
|
224
297
|
const queries = {
|
|
225
298
|
...DEFAULT_QUERY_CONFIG,
|
|
226
|
-
retry: createSafeRetryStrategy(
|
|
299
|
+
retry: createSafeRetryStrategy(maxRetries4xx, maxRetries5xx, maxRetriesOther),
|
|
227
300
|
retryDelay: exponentialBackoff,
|
|
228
301
|
refetchOnWindowFocus: disableFocus ? false : true,
|
|
229
302
|
refetchOnReconnect: disableReconnect ? false : true
|
|
@@ -236,7 +309,10 @@ export function createErrorSafeConfig(options = {}) {
|
|
|
236
309
|
queries.refetchInterval = conditionalRefetchInterval;
|
|
237
310
|
}
|
|
238
311
|
}
|
|
239
|
-
const mutations = {
|
|
312
|
+
const mutations = {
|
|
313
|
+
...DEFAULT_MUTATION_CONFIG,
|
|
314
|
+
retry: createSafeRetryStrategy(maxRetries4xx, maxRetries5xx, maxRetriesOther)
|
|
315
|
+
};
|
|
240
316
|
const result = {
|
|
241
317
|
queries: { ...queries, ...(overrides?.queries || {}) },
|
|
242
318
|
mutations: { ...mutations, ...(overrides?.mutations || {}) }
|
package/package.json
CHANGED