From ea20f564e5f33795959524d22a68f3ca4a305c1c Mon Sep 17 00:00:00 2001 From: Scott Hu Date: Sat, 12 Oct 2024 14:15:09 +0800 Subject: [PATCH] feat: alova@3.1.0 new features (#563) * feat: add inference for send arguments * feat: add type inference for send args in middleware & event * feat: add optional rest args for event and middleware context * fix(middleware): loading value depends on `immdediate` event if `middleware` is set * feat(usepagination): function `refresh` and `reload` return by `usePagination` will return a promise * test: correct test component * fix: method typo fix * fix: recover requesting to `setTimeout`, fix tests error * build: update changeset config --------- Co-authored-by: MeetinaXD --- .changeset/bright-ducks-hide.md | 5 + .changeset/config.json | 5 +- .changeset/cuddly-falcons-camp.md | 6 + .changeset/empty-singers-yell.md | 6 + .changeset/silly-glasses-glow.md | 5 + internal/testUtils.ts | 4 + packages/adapter-uniapp/src/index.ts | 1 - packages/adapter-uniapp/src/statesHook.ts | 4 +- .../test/requestAdapter.spec.ts | 4 +- packages/alova/src/Method.ts | 14 +- .../alova/typings/clienthook/general.d.ts | 139 +++++++------ .../hooks/actionDelegationMiddleware.d.ts | 7 +- .../clienthook/hooks/useAutoRequest.d.ts | 6 +- .../typings/clienthook/hooks/useCaptcha.d.ts | 9 +- .../typings/clienthook/hooks/useFetcher.d.ts | 11 +- .../typings/clienthook/hooks/useForm.d.ts | 18 +- .../clienthook/hooks/usePagination.d.ts | 33 ++-- .../typings/clienthook/hooks/useRequest.d.ts | 16 +- .../clienthook/hooks/useRetriable.d.ts | 21 +- .../clienthook/hooks/useSQRequest.d.ts | 29 +-- .../typings/clienthook/hooks/useSSE.d.ts | 40 ++-- .../clienthook/hooks/useSerialRequest.d.ts | 51 +++-- .../clienthook/hooks/useSerialWatcher.d.ts | 51 +++-- .../typings/clienthook/hooks/useWatcher.d.ts | 11 +- packages/alova/typings/index.d.ts | 35 +--- packages/client/src/event.ts | 80 +++++--- .../src/hooks/core/defaults/middleware.ts | 2 +- .../src/hooks/core/implements/createHook.ts | 8 +- .../core/implements/createRequestState.ts | 42 ++-- .../src/hooks/core/implements/stateCache.ts | 9 +- .../core/implements/useHookToSendRequest.ts | 47 +++-- packages/client/src/hooks/core/useFetcher.ts | 1 + packages/client/src/hooks/core/useRequest.ts | 12 +- packages/client/src/hooks/core/useWatcher.ts | 10 +- .../src/hooks/pagination/usePagination.ts | 187 ++++++++++-------- packages/client/src/hooks/serial/general.ts | 12 +- .../src/hooks/serial/useSerialRequest.ts | 8 +- .../src/hooks/serial/useSerialWatcher.ts | 8 +- .../silent/createSilentQueueMiddlewares.ts | 17 +- .../client/src/hooks/silent/silentQueue.ts | 2 +- .../client/src/hooks/silent/useSQRequest.ts | 8 +- packages/client/src/hooks/useAutoRequest.ts | 8 +- packages/client/src/hooks/useCaptcha.ts | 6 +- packages/client/src/hooks/useForm.ts | 12 +- .../client/src/hooks/useRetriableRequest.ts | 39 ++-- packages/client/src/hooks/useSSE.ts | 56 +++--- .../src/middlewares/actionDelegation.ts | 16 +- packages/client/src/statesHook/react.ts | 18 +- packages/client/src/statesHook/svelte.ts | 7 +- packages/client/src/statesHook/vue.ts | 6 +- .../test/react/components/Pagination.tsx | 32 ++- .../test/react/core/useWatcher.spec.tsx | 19 +- .../client/test/react/usePagination.spec.tsx | 10 +- .../test/react/useRetriableRequest.spec.tsx | 35 ++-- packages/client/test/react/useSSE.spec.tsx | 2 +- packages/client/test/type/exposure.spec.ts | 121 +++++++++++- .../client/test/vue-demi/useWatcher.spec.ts | 5 +- .../client/test/vue/components/pagination.vue | 35 +++- .../test/vue/core/hooks/useWatcher.spec.ts | 3 +- .../vue/core/middleware/useRequest.spec.ts | 40 ++-- .../vue/core/middleware/useWatcher.spec.ts | 4 +- .../client/test/vue/usePagination.spec.ts | 11 +- packages/client/test/vue/useSQRequest.spec.ts | 4 +- packages/client/test/vue/useSSE.spec.ts | 10 +- .../client/typings/clienthook/general.d.ts | 139 +++++++------ .../hooks/actionDelegationMiddleware.d.ts | 7 +- .../clienthook/hooks/useAutoRequest.d.ts | 6 +- .../typings/clienthook/hooks/useCaptcha.d.ts | 9 +- .../typings/clienthook/hooks/useFetcher.d.ts | 11 +- .../typings/clienthook/hooks/useForm.d.ts | 18 +- .../clienthook/hooks/usePagination.d.ts | 33 ++-- .../typings/clienthook/hooks/useRequest.d.ts | 16 +- .../clienthook/hooks/useRetriable.d.ts | 21 +- .../clienthook/hooks/useSQRequest.d.ts | 29 +-- .../typings/clienthook/hooks/useSSE.d.ts | 40 ++-- .../clienthook/hooks/useSerialRequest.d.ts | 51 +++-- .../clienthook/hooks/useSerialWatcher.d.ts | 51 +++-- .../typings/clienthook/hooks/useWatcher.d.ts | 11 +- packages/shared/src/event.ts | 22 +-- packages/shared/src/types.ts | 2 + packages/shared/src/vars.ts | 2 +- 81 files changed, 1186 insertions(+), 765 deletions(-) create mode 100644 .changeset/bright-ducks-hide.md create mode 100644 .changeset/cuddly-falcons-camp.md create mode 100644 .changeset/empty-singers-yell.md create mode 100644 .changeset/silly-glasses-glow.md diff --git a/.changeset/bright-ducks-hide.md b/.changeset/bright-ducks-hide.md new file mode 100644 index 00000000..58a3f2a7 --- /dev/null +++ b/.changeset/bright-ducks-hide.md @@ -0,0 +1,5 @@ +--- +'alova': minor +--- + +the function `refresh` and `reload` return by `usePagination` will return a promise diff --git a/.changeset/config.json b/.changeset/config.json index 121cb98b..b7b0f097 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,8 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "minor", - "ignore": ["@alova/client", "@alova/server", "react-demo", "vue-demo", "svelte-demo", "server-demo"] + "ignore": ["@alova/client", "@alova/server", "react-demo", "vue-demo", "svelte-demo", "server-demo"], + "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { + "onlyUpdatePeerDependentsWhenOutOfRange": true + } } diff --git a/.changeset/cuddly-falcons-camp.md b/.changeset/cuddly-falcons-camp.md new file mode 100644 index 00000000..5dc5aa45 --- /dev/null +++ b/.changeset/cuddly-falcons-camp.md @@ -0,0 +1,6 @@ +--- +'@alova/adapter-uniapp': patch +'alova': patch +--- + +loading value depends on `immdediate` event if `middleware` is set diff --git a/.changeset/empty-singers-yell.md b/.changeset/empty-singers-yell.md new file mode 100644 index 00000000..b236d20e --- /dev/null +++ b/.changeset/empty-singers-yell.md @@ -0,0 +1,6 @@ +--- +'@alova/shared': patch +'alova': patch +--- + +feat: add inference for send arguments diff --git a/.changeset/silly-glasses-glow.md b/.changeset/silly-glasses-glow.md new file mode 100644 index 00000000..c0e48103 --- /dev/null +++ b/.changeset/silly-glasses-glow.md @@ -0,0 +1,5 @@ +--- +'alova': patch +--- + +optimize the type `Method` to extend `Promise` diff --git a/internal/testUtils.ts b/internal/testUtils.ts index fa5b02b6..af5b92cb 100644 --- a/internal/testUtils.ts +++ b/internal/testUtils.ts @@ -52,3 +52,7 @@ export const generateContinuousNumbers = ( // eslint-disable-next-line @typescript-eslint/no-unused-vars export const expectType = (value: T) => {}; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const expectTrue = () => {}; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const expectAssignableBy = (value: T2) => {}; diff --git a/packages/adapter-uniapp/src/index.ts b/packages/adapter-uniapp/src/index.ts index 2be2f47f..84c173c1 100644 --- a/packages/adapter-uniapp/src/index.ts +++ b/packages/adapter-uniapp/src/index.ts @@ -6,7 +6,6 @@ import statesHook from './statesHook'; export { default as uniappL2CacheAdapter } from './l2CacheAdapter'; export { default as uniappMockResponse } from './mockResponse'; export { default as uniappRequestAdapter } from './requestAdapter'; - export default function AdapterUniapp({ mockRequest }: AdapterUniappOptions = {}) { return { statesHook, diff --git a/packages/adapter-uniapp/src/statesHook.ts b/packages/adapter-uniapp/src/statesHook.ts index 42245218..d33a43c8 100644 --- a/packages/adapter-uniapp/src/statesHook.ts +++ b/packages/adapter-uniapp/src/statesHook.ts @@ -6,9 +6,9 @@ export default { effectRequest(effectRequestParams, referingObject) { const { handler } = effectRequestParams; effectRequestParams.handler = (...args: any[]) => { - // 当没有参数时,表示为立即发送请求,此时延迟100ms让页面中的onLoad先执行 + // 当没有参数时,表示为立即发送请求,此时Promise.resolve让页面中的onLoad先执行 // 当有参数时,表示通过useWatcher状态改变时发送请求,此时直接调用handler即可 - len(args) > 0 ? handler(...args) : setTimeout(() => handler(...args), 100); + len(args) > 0 ? handler(...args) : Promise.resolve().then(() => handler(...args)); }; return VueHook.effectRequest(effectRequestParams, referingObject); } diff --git a/packages/adapter-uniapp/test/requestAdapter.spec.ts b/packages/adapter-uniapp/test/requestAdapter.spec.ts index 2b77837c..312bfe07 100644 --- a/packages/adapter-uniapp/test/requestAdapter.spec.ts +++ b/packages/adapter-uniapp/test/requestAdapter.spec.ts @@ -300,7 +300,7 @@ describe('request adapter', () => { await untilCbCalled(onError); expect(loading.value).toBeFalsy(); expect(data.value).toBeUndefined(); - expect(uploading.value).toEqual({ total: 200, loaded: 40 }); + expect(uploading.value).toEqual({ total: 200, loaded: 60 }); expect(error.value?.message).toBe('uploadFile:fail abort'); }); @@ -352,7 +352,7 @@ describe('request adapter', () => { await untilCbCalled(onError); expect(loading.value).toBeFalsy(); expect(data.value).toBeUndefined(); - expect(downloading.value).toEqual({ total: 200, loaded: 40 }); + expect(downloading.value).toEqual({ total: 200, loaded: 60 }); expect(error.value?.message).toBe('downloadFile:fail abort'); }); }); diff --git a/packages/alova/src/Method.ts b/packages/alova/src/Method.ts index cdb9e460..b236662a 100644 --- a/packages/alova/src/Method.ts +++ b/packages/alova/src/Method.ts @@ -69,6 +69,8 @@ export default class Method { public fromCache: boolean | undefined = undefinedValue; + [Symbol.toStringTag]: string; + constructor( type: MethodType, context: Alova, @@ -191,9 +193,9 @@ export default class Method { * @returns 返回一个Promise,用于执行任何回调 */ public then( - onfulfilled?: (value: AG['Responded']) => TResult1 | PromiseLike, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null - ) { + onfulfilled?: ((value: AG['Responded']) => TResult1 | PromiseLike) | null | undefined, + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined + ): Promise { return promiseThen(this.send(), onfulfilled, onrejected); } @@ -202,7 +204,9 @@ export default class Method { * @param onrejected 当Promise被reject时要执行的回调 * @returns 返回一个完成回调的Promise */ - public catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null) { + public catch( + onrejected?: ((reason: any) => TResult | PromiseLike) | null | undefined + ): Promise { return promiseCatch(this.send(), onrejected); } @@ -211,7 +215,7 @@ export default class Method { * @param onfinally Promise结算(resolve或reject)时执行的回调。 * @return 返回一个完成回调的Promise。 */ - public finally(onfinally?: (() => void) | undefined | null) { + public finally(onfinally?: (() => void) | null | undefined): Promise { return promiseFinally(this.send(), onfinally); } } diff --git a/packages/alova/typings/clienthook/general.d.ts b/packages/alova/typings/clienthook/general.d.ts index 55468753..e1671148 100644 --- a/packages/alova/typings/clienthook/general.d.ts +++ b/packages/alova/typings/clienthook/general.d.ts @@ -24,11 +24,11 @@ export interface StateMap { /** * alova base event */ -export interface AlovaEvent { +export interface AlovaEvent { /** * params from send function */ - args: any[]; + args: [...Args, ...any[]]; /** * current method instance */ @@ -37,17 +37,17 @@ export interface AlovaEvent { /** * success event object */ -export interface AlovaSuccessEvent extends AlovaEvent { +export interface AlovaSuccessEvent extends AlovaEvent { /** data数据是否来自缓存 */ fromCache: boolean; data: AG['Responded']; } /** 错误事件对象 */ -export interface AlovaErrorEvent extends AlovaEvent { +export interface AlovaErrorEvent extends AlovaEvent { error: any; } /** 完成事件对象 */ -export interface AlovaCompleteEvent extends AlovaEvent { +export interface AlovaCompleteEvent extends AlovaEvent { /** 响应状态 */ status: 'success' | 'error'; /** data数据是否来自缓存,当status为error时,fromCache始终为false */ @@ -73,18 +73,22 @@ export type StateUpdater, SE extends : ExportedStates[K]; }) => void; -export type AlovaMethodHandler = (...args: any[]) => Method; -export type SuccessHandler = (event: AlovaSuccessEvent) => void; -export type ErrorHandler = (event: AlovaErrorEvent) => void; -export type CompleteHandler = (event: AlovaCompleteEvent) => void; +export type AlovaMethodHandler = ( + ...args: Args +) => Method; +export type SuccessHandler = (event: AlovaSuccessEvent) => void; +export type ErrorHandler = (event: AlovaErrorEvent) => void; +export type CompleteHandler = ( + event: AlovaCompleteEvent +) => void; /** common hook configuration */ -export interface UseHookConfig { +export interface UseHookConfig { /** - * force request or not + * force request * @default false */ - force?: boolean | ((event: AlovaEvent) => boolean); + force?: boolean | ((event: AlovaEvent) => boolean); /** * refering object that sharing some value with this object. @@ -98,36 +102,39 @@ export interface UseHookConfig { } export interface AlovaMiddlewareContext { - /** 当前的method对象 */ + /** current method instance */ method: Method; - /** 命中的缓存数据 */ + /** + * cache data, only has value when hit cache + */ cachedResponse: AG['Responded'] | undefined; - /** 当前的usehook配置对象 */ + /** the config of current use hook */ config: any; - /** 中断函数 */ + /** abort request */ abort: UseHookExposure['abort']; } -/** 中间件next函数 */ -export interface MiddlewareNextGuardConfig { - force?: UseHookConfig['force']; +/** next function of middleware */ +export interface MiddlewareNextGuardConfig { + force?: UseHookConfig['force']; method?: Method; } /** * useRequest和useWatcher中间件的context参数 */ -export interface AlovaFrontMiddlewareContext extends AlovaMiddlewareContext { - /** 发送请求函数 */ - send: SendHandler; +export interface AlovaFrontMiddlewareContext + extends AlovaMiddlewareContext { + /** handler to send request */ + send: SendHandler; /** args 响应处理回调的参数,该参数由use hooks的send传入 */ - args: any[]; + args: [...Args, ...any[]]; - /** 前端状态集合 */ + /** state proxies set */ proxyStates: FrontRequestState< FrameworkState, FrameworkState, @@ -137,32 +144,38 @@ export interface AlovaFrontMiddlewareContext extends A >; /** - * 调用后将自定义控制loading的状态,内部不再触发loading状态的变更 - * 传入control为false时将取消控制 - * - * @param control 是否控制loading,默认为true + * custom control the state `loading` and doesn't toggle `loading` internally any more. + * call it with param `false` to cancel controlling. + * @JOU-amjs + * @param control whether to control loading, default is `true` */ controlLoading: (control?: boolean) => void; + + /** + * pass custom data + */ + [attr: string]: any; } /** - * alova useRequest/useWatcher中间件 + * alova useRequest/useWatcher middleware */ -export interface AlovaFrontMiddleware { - (context: AlovaFrontMiddlewareContext, next: AlovaGuardNext): any; +export interface AlovaFrontMiddleware { + (context: AlovaFrontMiddlewareContext, next: AlovaGuardNext): any; } /** - * useFetcher中间件的context参数 + * the context param of middleware in useFetcher */ -export interface AlovaFetcherMiddlewareContext extends AlovaMiddlewareContext { - /** 数据预加载函数 */ - fetch(method: Method, ...args: any[]): Promise; +export interface AlovaFetcherMiddlewareContext + extends AlovaMiddlewareContext { + /** fetch data */ + fetch(method: Method, ...args: [...Args, ...any[]]): Promise; /** args 响应处理回调的参数,该参数由useFetcher的fetch传入 */ - args: any[]; + args: [...Args, ...any[]]; - /** fetch状态的代理集合 */ + /** state proxies set */ proxyStates: FetchRequestState< FrameworkState, FrameworkState, @@ -171,19 +184,24 @@ export interface AlovaFetcherMiddlewareContext extends >; /** - * 调用后将自定义控制fetching的状态,内部不再触发fetching状态的变更 - * 传入control为false时将取消控制 - * - * @param control 是否控制fetching,默认为true + * custom control the state `loading` and doesn't toggle `loading` internally any more. + * call it with param `false` to cancel controlling. + * @JOU-amjs + * @param control whether to control loading, default is `true` */ - controlFetching: (control?: boolean) => void; + controlLoading: (control?: boolean) => void; + + /** + * pass custom data + */ + [attr: string]: any; } /** - * alova useRequest/useWatcher中间件 + * alova useFetcher middleware */ -export interface AlovaFetcherMiddleware { - (context: AlovaFetcherMiddlewareContext, next: AlovaGuardNext): any; +export interface AlovaFetcherMiddleware { + (context: AlovaFetcherMiddlewareContext, next: AlovaGuardNext): any; } export type ProxyStateGetter> = ( @@ -194,11 +212,11 @@ export type ProxyStateGetter> = < ? FrameworkReadableState : never; -export interface AlovaGuardNext { - (guardNextConfig?: MiddlewareNextGuardConfig): Promise; +export interface AlovaGuardNext { + (guardNextConfig?: MiddlewareNextGuardConfig): Promise; } -export type SendHandler = (...args: any[]) => Promise; +export type SendHandler = (...args: [...Args, ...any[]]) => Promise; export interface UseHookExportedState extends FrontRequestState< ExportedState, @@ -207,14 +225,17 @@ export interface UseHookExportedState ExportedState, ExportedState > {} -export interface UseHookExposure - extends UseHookExportedState { +export interface UseHookExposure< + AG extends AlovaGenerics = AlovaGenerics, + Args extends any[] = any[], + SelfType = unknown +> extends UseHookExportedState { abort: () => void; update: StateUpdater, AG['StatesExport']>; - send: SendHandler; - onSuccess(handler: SuccessHandler): IsUnknown; - onError(handler: ErrorHandler): IsUnknown; - onComplete(handler: CompleteHandler): IsUnknown; + send: SendHandler; + onSuccess(handler: SuccessHandler): IsUnknown; + onError(handler: ErrorHandler): IsUnknown; + onComplete(handler: CompleteHandler): IsUnknown; __proxyState: ProxyStateGetter>; __referingObj: ReferingObject; } @@ -224,7 +245,7 @@ export const enum EnumHookType { USE_WATCHER = 2, USE_FETCHER = 3 } -export interface Hook { +export interface Hook { /** 最后一次请求的method实例 */ m?: Method; @@ -245,16 +266,16 @@ export interface Hook { /** event manager */ em: EventManager<{ - success: AlovaSuccessEvent; - error: AlovaErrorEvent; - complete: AlovaCompleteEvent; + success: AlovaSuccessEvent; + error: AlovaErrorEvent; + complete: AlovaCompleteEvent; }>; /** hookType, useRequest=1, useWatcher=2, useFetcher=3 */ ht: EnumHookType; /** hook config */ - c: UseHookConfig; + c: UseHookConfig; /** refering object */ ro: ReferingObject; diff --git a/packages/alova/typings/clienthook/hooks/actionDelegationMiddleware.d.ts b/packages/alova/typings/clienthook/hooks/actionDelegationMiddleware.d.ts index d7fb40e0..1b3bf312 100644 --- a/packages/alova/typings/clienthook/hooks/actionDelegationMiddleware.d.ts +++ b/packages/alova/typings/clienthook/hooks/actionDelegationMiddleware.d.ts @@ -13,10 +13,13 @@ export interface Actions { * @param id 委托者id * @returns alova中间件函数 */ -export declare function actionDelegationMiddleware( +export declare function actionDelegationMiddleware< + AG extends AlovaGenerics = AlovaGenerics, + Args extends any[] = any[] +>( id: string | number | symbol ): ( - context: (AlovaFrontMiddlewareContext | AlovaFetcherMiddlewareContext) & { + context: (AlovaFrontMiddlewareContext | AlovaFetcherMiddlewareContext) & { delegatingActions?: Actions; }, next: AlovaGuardNext diff --git a/packages/alova/typings/clienthook/hooks/useAutoRequest.d.ts b/packages/alova/typings/clienthook/hooks/useAutoRequest.d.ts index 47d3198b..e6ad32ba 100644 --- a/packages/alova/typings/clienthook/hooks/useAutoRequest.d.ts +++ b/packages/alova/typings/clienthook/hooks/useAutoRequest.d.ts @@ -47,10 +47,10 @@ export type UnbindHandler = () => void; * @param config 配置参数 * @return useAutoRequest相关数据和操作函数 */ -export declare function useAutoRequest( - handler: Method | AlovaMethodHandler, +export declare function useAutoRequest( + handler: Method | AlovaMethodHandler, config?: AutoRequestHookConfig -): UseHookExposure; +): UseHookExposure; export declare namespace useAutoRequest { function onNetwork( notify: NotifyHandler, diff --git a/packages/alova/typings/clienthook/hooks/useCaptcha.d.ts b/packages/alova/typings/clienthook/hooks/useCaptcha.d.ts index 3b8907a3..5d11479b 100644 --- a/packages/alova/typings/clienthook/hooks/useCaptcha.d.ts +++ b/packages/alova/typings/clienthook/hooks/useCaptcha.d.ts @@ -16,7 +16,8 @@ export type CaptchaHookConfig = { /** * useCaptcha返回值 */ -export interface CaptchaExposure extends UseHookExposure> { +export interface CaptchaExposure + extends UseHookExposure> { /** * 当前倒计时,每秒-1,当倒计时到0时可再次发送验证码 */ @@ -29,7 +30,7 @@ export interface CaptchaExposure extends UseHookExposu * @param 配置参数 * @return useCaptcha相关数据和操作函数 */ -export declare function useCaptcha( - handler: Method | AlovaMethodHandler, +export declare function useCaptcha( + handler: Method | AlovaMethodHandler, config?: CaptchaHookConfig -): CaptchaExposure; +): CaptchaExposure; diff --git a/packages/alova/typings/clienthook/hooks/useFetcher.d.ts b/packages/alova/typings/clienthook/hooks/useFetcher.d.ts index c439acd5..2eb3d3c2 100644 --- a/packages/alova/typings/clienthook/hooks/useFetcher.d.ts +++ b/packages/alova/typings/clienthook/hooks/useFetcher.d.ts @@ -19,7 +19,8 @@ import { } from '../general'; /** - * 调用useFetcher时需要传入的类型,否则会导致状态类型错误 + * specify the alova type + * so that it can return the right states */ export type FetcherType> = { StatesExport: NonNullable extends StatesHook ? SE : any; @@ -27,9 +28,13 @@ export type FetcherType> = { /** useFetcher config export type */ export interface FetcherHookConfig extends UseHookConfig { - /** 中间件 */ + /** + * middleware + */ middleware?: AlovaFetcherMiddleware; - /** fetch是否同步更新data状态 */ + /** + * whether to update the corresponding states of fetching method instance + */ updateState?: boolean; } diff --git a/packages/alova/typings/clienthook/hooks/useForm.d.ts b/packages/alova/typings/clienthook/hooks/useForm.d.ts index 8c3080af..e0f138df 100644 --- a/packages/alova/typings/clienthook/hooks/useForm.d.ts +++ b/packages/alova/typings/clienthook/hooks/useForm.d.ts @@ -6,7 +6,10 @@ import { DataSerializer } from './useSQRequest'; /** * useForm的handler函数类型 */ -export type FormHookHandler = (form: F, ...args: any[]) => Method; +export type FormHookHandler = ( + form: F, + ...args: Args +) => Method; /** * useForm配置 @@ -59,7 +62,8 @@ export type RestoreHandler = () => void; /** * useForm返回值 */ -export interface FormExposure extends UseHookExposure> { +export interface FormExposure + extends UseHookExposure> { /** * 表单数据 */ @@ -94,7 +98,11 @@ export interface FormExposure extends UseHookExposu * @param config 配置参数 * @return useForm相关数据和操作函数 */ -export declare function useForm>( - handler: FormHookHandler, +export declare function useForm< + AG extends AlovaGenerics, + FormData extends Record, + Args extends any[] = any[] +>( + handler: FormHookHandler, config?: FormHookConfig -): FormExposure; +): FormExposure; diff --git a/packages/alova/typings/clienthook/hooks/usePagination.d.ts b/packages/alova/typings/clienthook/hooks/usePagination.d.ts index 14aa662a..654e582e 100644 --- a/packages/alova/typings/clienthook/hooks/usePagination.d.ts +++ b/packages/alova/typings/clienthook/hooks/usePagination.d.ts @@ -11,7 +11,9 @@ import { } from '../general'; import { WatcherHookConfig } from './useWatcher'; -/** @description usePagination相关 */ +/** + * @description usePagination相关 + */ export type ArgGetter = (data: R) => LD | undefined; export interface PaginationHookConfig extends WatcherHookConfig { /** @@ -55,8 +57,11 @@ export interface PaginationHookConfig extend */ watchingStates?: AG['StatesExport']['Watched'][]; } -export interface UsePaginationExposure - extends Omit>, 'update'> { +export interface UsePaginationExposure + extends Omit< + UseHookExposure>, + 'update' + > { page: ExportedState; pageSize: ExportedState; data: ExportedState< @@ -75,9 +80,15 @@ export interface UsePaginationExposure; isLastPage: ExportedComputed; fetching: ExportedState; - onFetchSuccess(handler: SuccessHandler): UsePaginationExposure; - onFetchError(handler: ErrorHandler): UsePaginationExposure; - onFetchComplete(handler: CompleteHandler): UsePaginationExposure; + onFetchSuccess( + handler: SuccessHandler + ): UsePaginationExposure; + onFetchError( + handler: ErrorHandler + ): UsePaginationExposure; + onFetchComplete( + handler: CompleteHandler + ): UsePaginationExposure; update: StateUpdater, AG['StatesExport']>; /** @@ -86,7 +97,7 @@ export interface UsePaginationExposure; /** * 插入一条数据 @@ -115,7 +126,7 @@ export interface UsePaginationExposure; } /** @@ -126,7 +137,7 @@ export interface UsePaginationExposure( - handler: (page: number, pageSize: number) => Method, +export declare function usePagination( + handler: (page: number, pageSize: number, ...args: Args) => Method, config?: PaginationHookConfig -): UsePaginationExposure; +): UsePaginationExposure; diff --git a/packages/alova/typings/clienthook/hooks/useRequest.d.ts b/packages/alova/typings/clienthook/hooks/useRequest.d.ts index 92c548ac..d0536fb2 100644 --- a/packages/alova/typings/clienthook/hooks/useRequest.d.ts +++ b/packages/alova/typings/clienthook/hooks/useRequest.d.ts @@ -3,7 +3,7 @@ import { AlovaFrontMiddleware, AlovaMethodHandler, UseHookConfig, UseHookExposur /** useRequest和useWatcher都有的类型 */ type InitialDataType = number | string | boolean | object; -export interface FrontRequestHookConfig extends UseHookConfig { +export interface FrontRequestHookConfig extends UseHookConfig { /** 是否立即发起一次请求 */ immediate?: boolean; @@ -13,12 +13,12 @@ export interface FrontRequestHookConfig extends UseHoo /** 额外的监管状态,可通过updateState更新 */ managedStates?: Record; - /** 中间件 */ - middleware?: AlovaFrontMiddleware; + /** middleware */ + middleware?: AlovaFrontMiddleware; } /** useRequest config export type */ -export type RequestHookConfig = FrontRequestHookConfig; +export type RequestHookConfig = FrontRequestHookConfig; /** * 自动管理响应状态hook @@ -30,7 +30,7 @@ export type RequestHookConfig = FrontRequestHookConfig * @param config 配置项 * @returns 响应式请求数据、操作函数及事件绑定函数 */ -export declare function useRequest( - methodHandler: Method | AlovaMethodHandler, - config?: RequestHookConfig -): UseHookExposure; +export declare function useRequest( + methodHandler: Method | AlovaMethodHandler, + config?: RequestHookConfig +): UseHookExposure; diff --git a/packages/alova/typings/clienthook/hooks/useRetriable.d.ts b/packages/alova/typings/clienthook/hooks/useRetriable.d.ts index cfaea10a..6f5cdd22 100644 --- a/packages/alova/typings/clienthook/hooks/useRetriable.d.ts +++ b/packages/alova/typings/clienthook/hooks/useRetriable.d.ts @@ -6,23 +6,23 @@ import { RequestHookConfig } from './useRequest'; /** * useRetriableRequest配置 */ -export type RetriableHookConfig = { +export type RetriableHookConfig = { /** * 最大重试次数,也可以设置为返回 boolean 值的函数,来动态判断是否继续重试。 * @default 3 */ - retry?: number | ((error: Error, ...args: any[]) => boolean); + retry?: number | ((error: Error, ...args: [...Args, ...any[]]) => boolean); /** * 避让策略 */ backoff?: BackoffPolicy; -} & RequestHookConfig; +} & RequestHookConfig; /** * useRetriableRequest onRetry回调事件实例 */ -export interface RetriableRetryEvent extends AlovaEvent { +export interface RetriableRetryEvent extends AlovaEvent { /** * 当前的重试次数 */ @@ -36,7 +36,7 @@ export interface RetriableRetryEvent extends AlovaEven /** * useRetriableRequest onFail回调事件实例 */ -export interface RetriableFailEvent extends AlovaErrorEvent { +export interface RetriableFailEvent extends AlovaErrorEvent { /** * 失败时的重试次数 */ @@ -45,7 +45,8 @@ export interface RetriableFailEvent extends AlovaError /** * useRetriableRequest返回值 */ -export interface RetriableExposure extends UseHookExposure> { +export interface RetriableExposure + extends UseHookExposure> { /** * 停止重试,只在重试期间调用有效 * 停止后将立即触发onFail事件 @@ -83,7 +84,7 @@ export interface RetriableExposure extends UseHookExpo * @param config 配置参数 * @return useRetriableRequest相关数据和操作函数 */ -export declare function useRetriableRequest( - handler: Method | AlovaMethodHandler, - config?: RetriableHookConfig -): RetriableExposure; +export declare function useRetriableRequest( + handler: Method | AlovaMethodHandler, + config?: RetriableHookConfig +): RetriableExposure; diff --git a/packages/alova/typings/clienthook/hooks/useSQRequest.d.ts b/packages/alova/typings/clienthook/hooks/useSQRequest.d.ts index a3486ceb..3a06a483 100644 --- a/packages/alova/typings/clienthook/hooks/useSQRequest.d.ts +++ b/packages/alova/typings/clienthook/hooks/useSQRequest.d.ts @@ -78,28 +78,28 @@ export interface GlobalSQFailEvent extends GlobalSQEve } /** SQ局部事件 */ -export interface ScopedSQEvent extends SQEvent { +export interface ScopedSQEvent extends SQEvent { /** * 通过send触发请求时传入的参数 */ - args: any[]; + args: [...Args, ...any[]]; } /** 局部成功事件 */ -export interface ScopedSQSuccessEvent extends ScopedSQEvent { +export interface ScopedSQSuccessEvent extends ScopedSQEvent { /** * 响应数据 */ data: AG['Responded']; } /** 局部失败事件 */ -export interface ScopedSQErrorEvent extends ScopedSQEvent { +export interface ScopedSQErrorEvent extends ScopedSQEvent { /** * 失败时抛出的错误 */ error: any; } /** 局部失败事件 */ -export interface ScopedSQRetryEvent extends ScopedSQEvent { +export interface ScopedSQRetryEvent extends ScopedSQEvent { /** * 重试次数 */ @@ -110,7 +110,7 @@ export interface ScopedSQRetryEvent extends ScopedSQEv retryDelay: number; } /** 局部完成事件 */ -export interface ScopedSQCompleteEvent extends ScopedSQEvent { +export interface ScopedSQCompleteEvent extends ScopedSQEvent { /** * 响应状态 */ @@ -315,7 +315,7 @@ export interface SQHookConfig { * 场景1. 手动开关 * 场景2. 网络状态不好时回退到static,网络状态自行判断 */ - behavior?: SQHookBehavior | ((event: AlovaEvent) => SQHookBehavior); + behavior?: SQHookBehavior | ((event: AlovaEvent) => SQHookBehavior); /** 重试错误规则 * 当错误符合以下表达式时才进行重试 @@ -334,7 +334,7 @@ export interface SQHookConfig { * 队列名,不传时选择默认队列 * 如需每次请求动态分配队列,可设为函数并返回队列名称 */ - queue?: string | ((event: AlovaEvent) => string); + queue?: string | ((event: AlovaEvent) => string); /** 静默提交时默认的响应数据 */ silentDefaultResponse?: () => any; @@ -348,15 +348,16 @@ export interface SQHookConfig { vDataCaptured?: (method: Method) => AG['Responded'] | undefined | void; } -export type SQRequestHookConfig = SQHookConfig & RequestHookConfig; +export type SQRequestHookConfig = SQHookConfig & + RequestHookConfig; export type FallbackHandler = (event: ScopedSQEvent) => void; export type RetryHandler = (event: ScopedSQRetryEvent) => void; export type BeforePushQueueHandler = ( event: ScopedSQEvent ) => void | boolean | Promise; export type PushedQueueHandler = (event: ScopedSQEvent) => void; -export type SQHookExposure = Omit< - UseHookExposure, +export type SQHookExposure = Omit< + UseHookExposure, 'onSuccess' | 'onError' | 'onComplete' > & { /** @@ -458,10 +459,10 @@ export type SilentQueueMap = Record[]>; * 带silentQueue的request hook * silentQueue是实现静默提交的核心部件,其中将用于存储silentMethod实例,它们将按顺序串行发送提交 */ -export declare function useSQRequest( - handler: AlovaMethodHandler, +export declare function useSQRequest( + handler: AlovaMethodHandler, config?: SQRequestHookConfig -): SQHookExposure; +): SQHookExposure; export declare function bootSilentFactory(options: SilentFactoryBootOptions): void; export declare function onSilentSubmitBoot(handler: SilentSubmitBootHandler): OffEventCallback; export declare function onSilentSubmitSuccess(handler: SilentSubmitSuccessHandler): OffEventCallback; diff --git a/packages/alova/typings/clienthook/hooks/useSSE.d.ts b/packages/alova/typings/clienthook/hooks/useSSE.d.ts index 1b155239..18efbc5c 100644 --- a/packages/alova/typings/clienthook/hooks/useSSE.d.ts +++ b/packages/alova/typings/clienthook/hooks/useSSE.d.ts @@ -7,22 +7,30 @@ export const enum SSEHookReadyState { CLOSED = 2 } -export interface AlovaSSEEvent extends AlovaEvent { +export interface AlovaSSEEvent extends AlovaEvent { method: Method; eventSource: EventSource; // eventSource实例 } -export interface AlovaSSEErrorEvent extends AlovaSSEEvent { +export interface AlovaSSEErrorEvent + extends AlovaSSEEvent { error: Error; // 错误对象 } -export interface AlovaSSEMessageEvent extends AlovaSSEEvent { +export interface AlovaSSEMessageEvent + extends AlovaSSEEvent { data: Data; // 每次响应的,经过拦截器转换后的数据 } -export type SSEOnOpenTrigger = (event: AlovaSSEEvent) => void; -export type SSEOnMessageTrigger = (event: AlovaSSEMessageEvent) => void; -export type SSEOnErrorTrigger = (event: AlovaSSEErrorEvent) => void; -export type SSEOn = ( +export type SSEOnOpenTrigger = ( + event: AlovaSSEEvent +) => void; +export type SSEOnMessageTrigger = ( + event: AlovaSSEMessageEvent +) => void; +export type SSEOnErrorTrigger = ( + event: AlovaSSEErrorEvent +) => void; +export type SSEOn = ( eventName: string, - handler: (event: AlovaSSEMessageEvent) => void + handler: (event: AlovaSSEMessageEvent) => void ) => () => void; /** @@ -62,7 +70,7 @@ export interface SSEHookConfig { /** * useSSE() 返回类型 */ -export interface SSEExposure { +export interface SSEExposure { readyState: ExportedState; data: ExportedState; eventSource: ExportedState; @@ -70,7 +78,7 @@ export interface SSEExposure { * 手动发起请求。在使用 `immediate: true` 时该方法会自动触发 * @param args 请求参数,会传递给 method */ - send(...args: any[]): Promise; + send(...args: [...Args, ...any[]]): Promise; /** * 关闭连接 */ @@ -80,21 +88,21 @@ export interface SSEExposure { * @param callback 回调函数 * @returns 取消注册函数 */ - onOpen(callback: SSEOnOpenTrigger): this; + onOpen(callback: SSEOnOpenTrigger): this; /** * 注册 EventSource message 的回调函数 * @param callback 回调函数 * @returns 取消注册函数 */ - onMessage(callback: SSEOnMessageTrigger): this; + onMessage(callback: SSEOnMessageTrigger): this; /** * 注册 EventSource error 的回调函数 * @param callback 回调函数 * @returns 取消注册函数 */ - onError(callback: SSEOnErrorTrigger): this; + onError(callback: SSEOnErrorTrigger): this; /** * @param eventName 事件名称,默认存在 `open` | `error` | `message` @@ -112,7 +120,7 @@ export interface SSEExposure { * @param config 配置参数 * @return useSSE相关数据和操作函数 */ -export declare function useSSE( - handler: Method | AlovaMethodHandler, +export declare function useSSE( + handler: Method | AlovaMethodHandler, config?: SSEHookConfig -): SSEExposure; +): SSEExposure; diff --git a/packages/alova/typings/clienthook/hooks/useSerialRequest.d.ts b/packages/alova/typings/clienthook/hooks/useSerialRequest.d.ts index d4664d32..215389e5 100644 --- a/packages/alova/typings/clienthook/hooks/useSerialRequest.d.ts +++ b/packages/alova/typings/clienthook/hooks/useSerialRequest.d.ts @@ -10,10 +10,10 @@ import { RequestHookConfig } from './useRequest'; * @param config 配置参数 * @return useSerialRequest相关数据和操作函数 */ -export declare function useSerialRequest( - serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], +export declare function useSerialRequest( + serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialRequest(重载) @@ -23,10 +23,14 @@ export declare function useSerialRequest( * @param config 配置参数 * @return useSerialRequest相关数据和操作函数 */ -export declare function useSerialRequest( - serialHandlers: [Method | AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[]], +export declare function useSerialRequest< + AG extends AlovaGenerics, + AG2 extends AlovaGenerics, + Args extends any[] = any[] +>( + serialHandlers: [Method | AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[]], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialRequest(重载) @@ -39,16 +43,17 @@ export declare function useSerialRequest( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[] ], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialRequest(重载) @@ -62,17 +67,18 @@ export declare function useSerialRequest< AG extends AlovaGenerics, AG2 extends AlovaGenerics, AG3 extends AlovaGenerics, - AG4 extends AlovaGenerics + AG4 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[] ], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialRequest(重载) @@ -87,10 +93,11 @@ export declare function useSerialRequest< AG2 extends AlovaGenerics, AG3 extends AlovaGenerics, AG4 extends AlovaGenerics, - AG5 extends AlovaGenerics + AG5 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -98,7 +105,7 @@ export declare function useSerialRequest< ...AlovaMethodHandler[] ], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialRequest(重载) @@ -114,10 +121,11 @@ export declare function useSerialRequest< AG3 extends AlovaGenerics, AG4 extends AlovaGenerics, AG5 extends AlovaGenerics, - AG6 extends AlovaGenerics + AG6 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -126,7 +134,7 @@ export declare function useSerialRequest< ...AlovaMethodHandler[] ], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialRequest(重载) @@ -143,10 +151,11 @@ export declare function useSerialRequest< AG4 extends AlovaGenerics, AG5 extends AlovaGenerics, AG6 extends AlovaGenerics, - AG7 extends AlovaGenerics + AG7 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -156,4 +165,4 @@ export declare function useSerialRequest< ...AlovaMethodHandler[] ], config?: RequestHookConfig -): UseHookExposure; +): UseHookExposure; diff --git a/packages/alova/typings/clienthook/hooks/useSerialWatcher.d.ts b/packages/alova/typings/clienthook/hooks/useSerialWatcher.d.ts index ba0b11b2..5402ccec 100644 --- a/packages/alova/typings/clienthook/hooks/useSerialWatcher.d.ts +++ b/packages/alova/typings/clienthook/hooks/useSerialWatcher.d.ts @@ -10,11 +10,11 @@ import { WatcherHookConfig } from './useWatcher'; * @param config 配置参数 * @return useSerialWatcher相关数据和操作函数 */ -export declare function useSerialWatcher( - serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], +export declare function useSerialWatcher( + serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialWatcher(重载) @@ -24,11 +24,15 @@ export declare function useSerialWatcher( * @param config 配置参数 * @return useSerialWatcher相关数据和操作函数 */ -export declare function useSerialWatcher( - serialHandlers: [Method | AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[]], +export declare function useSerialWatcher< + AG extends AlovaGenerics, + AG2 extends AlovaGenerics, + Args extends any[] = any[] +>( + serialHandlers: [Method | AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[]], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialWatcher(重载) @@ -41,17 +45,18 @@ export declare function useSerialWatcher( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, ...AlovaMethodHandler[] ], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialWatcher(重载) @@ -65,10 +70,11 @@ export declare function useSerialWatcher< AG extends AlovaGenerics, AG2 extends AlovaGenerics, AG3 extends AlovaGenerics, - AG4 extends AlovaGenerics + AG4 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -76,7 +82,7 @@ export declare function useSerialWatcher< ], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialWatcher(重载) @@ -91,10 +97,11 @@ export declare function useSerialWatcher< AG2 extends AlovaGenerics, AG3 extends AlovaGenerics, AG4 extends AlovaGenerics, - AG5 extends AlovaGenerics + AG5 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -103,7 +110,7 @@ export declare function useSerialWatcher< ], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialWatcher(重载) @@ -119,10 +126,11 @@ export declare function useSerialWatcher< AG3 extends AlovaGenerics, AG4 extends AlovaGenerics, AG5 extends AlovaGenerics, - AG6 extends AlovaGenerics + AG6 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -132,7 +140,7 @@ export declare function useSerialWatcher< ], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; /** * useSerialWatcher(重载) @@ -149,10 +157,11 @@ export declare function useSerialWatcher< AG4 extends AlovaGenerics, AG5 extends AlovaGenerics, AG6 extends AlovaGenerics, - AG7 extends AlovaGenerics + AG7 extends AlovaGenerics, + Args extends any[] = any[] >( serialHandlers: [ - Method | AlovaMethodHandler, + Method | AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, AlovaMethodHandler, @@ -163,4 +172,4 @@ export declare function useSerialWatcher< ], watchingStates: AG['StatesExport']['Watched'][], config?: WatcherHookConfig -): UseHookExposure; +): UseHookExposure; diff --git a/packages/alova/typings/clienthook/hooks/useWatcher.d.ts b/packages/alova/typings/clienthook/hooks/useWatcher.d.ts index c75a7879..a1f1fc64 100644 --- a/packages/alova/typings/clienthook/hooks/useWatcher.d.ts +++ b/packages/alova/typings/clienthook/hooks/useWatcher.d.ts @@ -3,7 +3,8 @@ import { AlovaMethodHandler, UseHookExposure } from '../general'; import { FrontRequestHookConfig } from './useRequest'; /** useWatcher config export type */ -export interface WatcherHookConfig extends FrontRequestHookConfig { +export interface WatcherHookConfig + extends FrontRequestHookConfig { /** 请求防抖时间(毫秒),传入数组时可按watchingStates的顺序单独设置防抖时间 */ debounce?: number | number[]; abortLast?: boolean; @@ -20,8 +21,8 @@ export interface WatcherHookConfig extends FrontReques * @param config 配置项 * @returns 响应式请求数据、操作函数及事件绑定函数 */ -export declare function useWatcher( - methodHandler: Method | AlovaMethodHandler, +export declare function useWatcher( + methodHandler: Method | AlovaMethodHandler, watchingStates: AG['StatesExport']['Watched'][], - config?: WatcherHookConfig -): UseHookExposure; + config?: WatcherHookConfig +): UseHookExposure; diff --git a/packages/alova/typings/index.d.ts b/packages/alova/typings/index.d.ts index 9078d9ef..172bf5ff 100644 --- a/packages/alova/typings/index.d.ts +++ b/packages/alova/typings/index.d.ts @@ -457,7 +457,7 @@ export interface AbortFunction { /** * 请求方法类型 */ -export declare class Method { +export declare class Method extends Promise { constructor( type: MethodType, context: Alova, @@ -558,33 +558,6 @@ export declare class Method { */ abort: AbortFunction; - /** - * 绑定resolve和/或reject Promise的callback - * @param onfulfilled resolve Promise时要执行的回调 - * @param onrejected 当Promise被reject时要执行的回调 - * @returns 返回一个Promise,用于执行任何回调 - */ - then( - onfulfilled?: (value: AG['Responded']) => TResult1 | PromiseLike, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null - ): Promise; - - /** - * 绑定一个仅用于reject Promise的回调 - * @param onrejected 当Promise被reject时要执行的回调 - * @returns 返回一个完成回调的Promise - */ - catch( - onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null - ): Promise; - - /** - * 绑定一个回调,该回调在Promise结算(resolve或reject)时调用 - * @param onfinally Promise结算(resolve或reject)时执行的回调。 - * @return 返回一个完成回调的Promise。 - */ - finally(onfinally?: (() => void) | undefined | null): Promise; - /** * 绑定下载进度回调函数 * @param progressHandler 下载进度回调函数 @@ -608,6 +581,7 @@ export class MethodSnapshotContainer { capacity: number; occupy: number; + save(methodInstance: Method): void; /** @@ -657,11 +631,6 @@ export interface Alova { data?: RequestBody, config?: AlovaMethodCreateConfig ): Method>; - Put( - url: string, - data?: RequestBody, - config?: AlovaMethodCreateConfig - ): Method>; Head( url: string, config?: AlovaMethodCreateConfig diff --git a/packages/client/src/event.ts b/packages/client/src/event.ts index 91b40228..4aa536bd 100644 --- a/packages/client/src/event.ts +++ b/packages/client/src/event.ts @@ -17,28 +17,31 @@ import { SilentMethod } from '~/typings/clienthook'; -export class AlovaSSEEvent extends AlovaEventBase { +export class AlovaSSEEvent extends AlovaEventBase { eventSource: EventSource; // eventSource实例 - constructor(base: AlovaEventBase, eventSource: EventSource) { + constructor(base: AlovaEventBase, eventSource: EventSource) { super(base.method, base.args); this.eventSource = eventSource; } } -export class AlovaSSEErrorEvent extends AlovaSSEEvent { +export class AlovaSSEErrorEvent extends AlovaSSEEvent { error: Error; // 错误对象 - constructor(base: AlovaSSEEvent, error: Error) { + constructor(base: AlovaSSEEvent, error: Error) { super(base, base.eventSource); this.error = error; } } -export class AlovaSSEMessageEvent extends AlovaSSEEvent { +export class AlovaSSEMessageEvent extends AlovaSSEEvent< + AG, + Args +> { data: Data; // 每次响应的,经过拦截器转换后的数据 - constructor(base: AlovaSSEEvent, data: Data) { + constructor(base: AlovaSSEEvent, data: Data) { super(base, base.eventSource); this.data = data; } @@ -170,21 +173,24 @@ export class GlobalSQFailEvent extends GlobalSQEvent extends SQEvent implements IScopedSQEvent { +export class ScopedSQEvent + extends SQEvent + implements IScopedSQEvent +{ /** * 通过send触发请求时传入的参数 */ - args: any[]; + args: [...Args, ...any[]]; - constructor(behavior: SQHookBehavior, method: Method, silentMethod: SilentMethod, args: any[]) { + constructor(behavior: SQHookBehavior, method: Method, silentMethod: SilentMethod, args: [...Args, ...any[]]) { super(behavior, method, silentMethod); this.args = args; } } -export class ScopedSQSuccessEvent - extends ScopedSQEvent - implements IScopedSQSuccessEvent +export class ScopedSQSuccessEvent + extends ScopedSQEvent + implements IScopedSQSuccessEvent { /** * 响应数据 @@ -195,7 +201,7 @@ export class ScopedSQSuccessEvent behavior: SQHookBehavior, method: Method, silentMethod: SilentMethod, - args: any[], + args: [...Args, ...any[]], data: AG['Responded'] ) { super(behavior, method, silentMethod, args); @@ -203,19 +209,31 @@ export class ScopedSQSuccessEvent } } -export class ScopedSQErrorEvent extends ScopedSQEvent implements IScopedSQErrorEvent { +export class ScopedSQErrorEvent + extends ScopedSQEvent + implements IScopedSQErrorEvent +{ /** * 失败时抛出的错误 */ error: any; - constructor(behavior: SQHookBehavior, method: Method, silentMethod: SilentMethod, args: any[], error: any) { + constructor( + behavior: SQHookBehavior, + method: Method, + silentMethod: SilentMethod, + args: [...Args, ...any[]], + error: any + ) { super(behavior, method, silentMethod, args); this.error = error; } } -export class ScopedSQRetryEvent extends ScopedSQEvent implements IScopedSQRetryEvent { +export class ScopedSQRetryEvent + extends ScopedSQEvent + implements IScopedSQRetryEvent +{ /** * 重试次数 */ @@ -230,7 +248,7 @@ export class ScopedSQRetryEvent extends ScopedSQEvent< behavior: SQHookBehavior, method: Method, silentMethod: SilentMethod, - args: any[], + args: [...Args, ...any[]], retryTimes: number, retryDelay: number ) { @@ -240,14 +258,14 @@ export class ScopedSQRetryEvent extends ScopedSQEvent< } } -export class ScopedSQCompleteEvent - extends ScopedSQEvent - implements IScopedSQCompleteEvent +export class ScopedSQCompleteEvent + extends ScopedSQEvent + implements IScopedSQCompleteEvent { /** * 响应状态 */ - status: AlovaCompleteEvent['status']; + status: AlovaCompleteEvent['status']; /** * 响应数据 @@ -263,8 +281,8 @@ export class ScopedSQCompleteEvent behavior: SQHookBehavior, method: Method, silentMethod: SilentMethod, - args: any[], - status: AlovaCompleteEvent['status'], + args: [...Args, ...any[]], + status: AlovaCompleteEvent['status'], data?: AG['Responded'], error?: any ) { @@ -275,9 +293,9 @@ export class ScopedSQCompleteEvent } } -export class RetriableRetryEvent - extends AlovaEventBase - implements IRetriableRetryEvent +export class RetriableRetryEvent + extends AlovaEventBase + implements IRetriableRetryEvent { /** * 当前的重试次数 @@ -289,23 +307,23 @@ export class RetriableRetryEvent */ retryDelay: number; - constructor(base: AlovaEventBase, retryTimes: number, retryDelay: number) { + constructor(base: AlovaEventBase, retryTimes: number, retryDelay: number) { super(base.method, base.args); this.retryTimes = retryTimes; this.retryDelay = retryDelay; } } -export class RetriableFailEvent - extends AlovaErrorEvent - implements IRetriableFailEvent +export class RetriableFailEvent + extends AlovaErrorEvent + implements IRetriableFailEvent { /** * 失败时的重试次数 */ retryTimes: number; - constructor(base: AlovaEventBase, error: any, retryTimes: number) { + constructor(base: AlovaEventBase, error: any, retryTimes: number) { super(base, error); this.retryTimes = retryTimes; } diff --git a/packages/client/src/hooks/core/defaults/middleware.ts b/packages/client/src/hooks/core/defaults/middleware.ts index da6120ed..b00fe109 100644 --- a/packages/client/src/hooks/core/defaults/middleware.ts +++ b/packages/client/src/hooks/core/defaults/middleware.ts @@ -1,5 +1,5 @@ import type { AlovaGenerics } from 'alova'; import { AlovaGuardNext } from '~/typings/clienthook'; -const defaultMiddleware = (_: any, next: AlovaGuardNext) => next(); +const defaultMiddleware = (_: any, next: AlovaGuardNext) => next(); export default defaultMiddleware; diff --git a/packages/client/src/hooks/core/implements/createHook.ts b/packages/client/src/hooks/core/implements/createHook.ts index 7a1da55b..ebbc5afc 100644 --- a/packages/client/src/hooks/core/implements/createHook.ts +++ b/packages/client/src/hooks/core/implements/createHook.ts @@ -4,10 +4,10 @@ import type { AlovaGenerics, FrontRequestState, Progress, ReferingObject } from import { Method } from 'alova'; import { Hook, EnumHookType as TEnumHookType, UseHookConfig } from '~/typings/clienthook'; -export default ( +export default ( ht: TEnumHookType, - c: UseHookConfig, - eventManager: Hook['em'], + c: UseHookConfig, + eventManager: Hook['em'], ro: ReferingObject ) => ({ @@ -43,4 +43,4 @@ export default ( /** managedStates */ ms: {} - }) as Hook; + }) as Hook; diff --git a/packages/client/src/hooks/core/implements/createRequestState.ts b/packages/client/src/hooks/core/implements/createRequestState.ts index 2e74bdee..9ff9a02a 100644 --- a/packages/client/src/hooks/core/implements/createRequestState.ts +++ b/packages/client/src/hooks/core/implements/createRequestState.ts @@ -46,25 +46,28 @@ const refCurrent = (ref: { current: T }) => ref.current; * @param debounceDelay 请求发起的延迟时间 * @returns 当前的请求状态、操作函数及事件绑定函数 */ -export default function createRequestState>( +export default function createRequestState< + AG extends AlovaGenerics, + Args extends any[], + Config extends UseHookConfig +>( hookType: EnumHookType, - methodHandler: Method | AlovaMethodHandler, + methodHandler: Method | AlovaMethodHandler, useHookConfig: Config, - initialData?: FrontRequestHookConfig['initialData'], + initialData?: FrontRequestHookConfig['initialData'], immediate = falseValue, watchingStates?: AG['StatesExport']['Watched'][], debounceDelay: WatcherHookConfig['debounce'] = 0 ) { // shallow clone config object to avoid passing the same useHookConfig object which may cause vue2 state update error useHookConfig = { ...useHookConfig }; - const { middleware, __referingObj: referingObject = { trackedKeys: {}, bindError: falseValue } } = useHookConfig; - let initialLoading = middleware ? falseValue : !!immediate; + const { __referingObj: referingObject = { trackedKeys: {}, bindError: falseValue } } = useHookConfig; + let initialLoading = !!immediate; // 当立即发送请求时,需要通过是否强制请求和是否有缓存来确定初始loading值,这样做有以下两个好处: // 1. 在react下立即发送请求可以少渲染一次 // 2. SSR渲染的html中,其初始视图为loading状态的,避免在客户端展现时的loading视图闪动 - // 3. 如果config.middleware中设置了`controlLoading`时,需要默认为false,但这边无法确定middleware中是否有调用`controlLoading`,因此条件只能放宽点,当有`config.middleware`时则初始`loading`为false - if (immediate && !middleware) { + if (immediate) { // 调用getHandlerMethod时可能会报错,需要try/catch try { const methodInstance = getHandlerMethod(methodHandler, coreHookAssert(hookType)); @@ -82,7 +85,7 @@ export default function createRequestState).force ?? falseValue); + const forceRequestFinally = sloughConfig((useHookConfig as UseHookConfig).force ?? falseValue); initialLoading = !!forceRequestFinally || !cachedResponse; } catch (error) {} } @@ -96,7 +99,7 @@ export default function createRequestState; + const { managedStates = {} } = useHookConfig as FrontRequestHookConfig; const managedStatesProxy = mapObject(managedStates, (state, key) => transformState2Proxy(state, key)); const data = create((isFn(initialData) ? initialData() : initialData) as AG['Responded'], 'data'); const loading = create(initialLoading, 'loading'); @@ -106,9 +109,9 @@ export default function createRequestState; - error: AlovaErrorEvent; - complete: AlovaCompleteEvent; + success: AlovaSuccessEvent; + error: AlovaErrorEvent; + complete: AlovaCompleteEvent; }>(); const hookInstance = refCurrent(ref(createHook(hookType, useHookConfig, eventManager, referingObject))); @@ -124,8 +127,10 @@ export default function createRequestState | AlovaMethodHandler = methodHandler, sendCallingArgs?: any[]) => - useHookToSendRequest(hookInstance, handler, sendCallingArgs) as Promise; + const handleRequest = ( + handler: Method | AlovaMethodHandler = methodHandler, + sendCallingArgs?: [...Args, ...any[]] + ) => useHookToSendRequest(hookInstance, handler, sendCallingArgs) as Promise; // 以捕获异常的方式调用handleRequest // 捕获异常避免异常继续向外抛出 const wrapEffectRequest = (ro = referingObject, handler?: Method | AlovaMethodHandler) => @@ -175,17 +180,18 @@ export default function createRequestState) => handleRequest(methodInstance, sendCallingArgs), - onSuccess(handler: SuccessHandler) { + send: (sendCallingArgs?: [...Args, ...any[]], methodInstance?: Method) => + handleRequest(methodInstance, sendCallingArgs), + onSuccess(handler: SuccessHandler) { eventManager.on(KEY_SUCCESS, handler); }, - onError(handler: ErrorHandler) { + onError(handler: ErrorHandler) { // will not throw error when bindError is true. // it will reset in `exposeProvider` so that ignore the error binding in custom use hooks. referingObject.bindError = trueValue; eventManager.on(KEY_ERROR, handler); }, - onComplete(handler: CompleteHandler) { + onComplete(handler: CompleteHandler) { eventManager.on(KEY_COMPLETE, handler); } }); diff --git a/packages/client/src/hooks/core/implements/stateCache.ts b/packages/client/src/hooks/core/implements/stateCache.ts index 8e4d62f9..74f62d04 100644 --- a/packages/client/src/hooks/core/implements/stateCache.ts +++ b/packages/client/src/hooks/core/implements/stateCache.ts @@ -26,11 +26,16 @@ export const getStateCache = (namespace: string, key: string) => { * @param key 请求key值 * @param data 缓存数据 */ -export const setStateCache = (namespace: string, key: string, data: MergedStatesMap, hookInstance: Hook) => { +export const setStateCache = ( + namespace: string, + key: string, + data: MergedStatesMap, + hookInstance: Hook +) => { const cachedState = (stateCache[namespace] = stateCache[namespace] || {}); cachedState[key] = { s: data, - h: hookInstance + h: hookInstance as Hook }; }; diff --git a/packages/client/src/hooks/core/implements/useHookToSendRequest.ts b/packages/client/src/hooks/core/implements/useHookToSendRequest.ts index 359511e0..9eefa007 100644 --- a/packages/client/src/hooks/core/implements/useHookToSendRequest.ts +++ b/packages/client/src/hooks/core/implements/useHookToSendRequest.ts @@ -33,10 +33,10 @@ import { getStateCache, removeStateCache, setStateCache } from './stateCache'; * @param sendCallingArgs send函数参数 * @returns 请求状态 */ -export default function useHookToSendRequest( - hookInstance: Hook, - methodHandler: Method | AlovaMethodHandler, - sendCallingArgs: any[] = [] +export default function useHookToSendRequest( + hookInstance: Hook, + methodHandler: Method | AlovaMethodHandler, + sendCallingArgs: [...Args, ...any[]] = [] as any ) { const currentHookAssert = coreHookAssert(hookInstance.ht); let methodInstance = getHandlerMethod(methodHandler, currentHookAssert, sendCallingArgs); @@ -44,13 +44,14 @@ export default function useHookToSendRequest( const { loading: loadingState, data: dataState, error: errorState } = frontStates; const isFetcher = hookType === EnumHookType.USE_FETCHER; const { force: forceRequest = falseValue, middleware = defaultMiddleware } = useHookConfig as - | FrontRequestHookConfig + | FrontRequestHookConfig | FetcherHookConfig; const alovaInstance = getContext(methodInstance); const { id } = alovaInstance; // 如果是静默请求,则请求后直接调用onSuccess,不触发onError,然后也不会更新progress const methodKey = getMethodInternalKey(methodInstance); const { abortLast = trueValue } = useHookConfig as WatcherHookConfig; + const isFirstRequest = !hookInstance.m; hookInstance.m = methodInstance; return (async () => { @@ -75,12 +76,12 @@ export default function useHookToSendRequest( } // 中间件函数next回调函数,允许修改强制请求参数,甚至替换即将发送请求的Method实例 - const guardNext: AlovaGuardNext = guardNextConfig => { + const guardNext: AlovaGuardNext = guardNextConfig => { isNextCalled = trueValue; const { force: guardNextForceRequest = forceRequest, method: guardNextReplacingMethod = methodInstance } = guardNextConfig || {}; const forceRequestFinally = sloughConfig(guardNextForceRequest, [ - newInstance(AlovaEventBase, methodInstance, sendCallingArgs) + newInstance(AlovaEventBase, methodInstance, sendCallingArgs) ]); const progressUpdater = (stage: 'downloading' | 'uploading') => @@ -121,9 +122,17 @@ export default function useHookToSendRequest( // 是否需要更新响应数据,以及调用响应回调 const toUpdateResponse = () => hookType !== EnumHookType.USE_WATCHER || !abortLast || hookInstance.m === methodInstance; + + const controlLoading = (control = trueValue) => { + // only reset loading state in first request + if (control && isFirstRequest) { + loadingState.v = falseValue; + } + controlledLoading = control; + }; // 调用中间件函数 const middlewareCompletePromise = isFetcher - ? (middleware as AlovaFetcherMiddleware)( + ? (middleware as AlovaFetcherMiddleware)( { ...commonContext, args: sendCallingArgs, @@ -132,27 +141,23 @@ export default function useHookToSendRequest( return useHookToSendRequest(hookInstance, methodInstance as Method, args); }, proxyStates: omit(frontStates, 'data'), - controlFetching(control = trueValue) { - controlledLoading = control; - } + controlLoading }, guardNext ) - : (middleware as AlovaFrontMiddleware)( + : (middleware as AlovaFrontMiddleware)( { ...commonContext, args: sendCallingArgs, - send: (...args) => useHookToSendRequest(hookInstance, methodHandler, args), + send: (...args) => useHookToSendRequest(hookInstance, methodHandler, args as any), proxyStates: frontStates, - controlLoading(control = trueValue) { - controlledLoading = control; - } + controlLoading }, guardNext ); let finallyResponse: any = undefinedValue; - const baseEvent = AlovaEventBase.spawn(methodInstance, sendCallingArgs); + const baseEvent = (AlovaEventBase).spawn(methodInstance, sendCallingArgs); try { // 统一处理响应 const middlewareReturnedData = await middlewareCompletePromise; @@ -171,10 +176,10 @@ export default function useHookToSendRequest( errorState.v = undefinedValue; // loading状态受控时将不再更改为false !controlledLoading && (loadingState.v = falseValue); - hookInstance.em.emit(KEY_SUCCESS, newInstance(AlovaSuccessEvent, baseEvent, data, fromCache())); + hookInstance.em.emit(KEY_SUCCESS, newInstance(AlovaSuccessEvent, baseEvent, data, fromCache())); hookInstance.em.emit( KEY_COMPLETE, - newInstance(AlovaCompleteEvent, baseEvent, KEY_SUCCESS, data, fromCache(), undefinedValue) + newInstance(AlovaCompleteEvent, baseEvent, KEY_SUCCESS, data, fromCache(), undefinedValue) ); } return data; @@ -201,10 +206,10 @@ export default function useHookToSendRequest( errorState.v = error; // loading状态受控时将不再更改为false !controlledLoading && (loadingState.v = falseValue); - hookInstance.em.emit(KEY_ERROR, newInstance(AlovaErrorEvent, baseEvent, error)); + hookInstance.em.emit(KEY_ERROR, newInstance(AlovaErrorEvent, baseEvent, error)); hookInstance.em.emit( KEY_COMPLETE, - newInstance(AlovaCompleteEvent, baseEvent, KEY_ERROR, undefinedValue, fromCache(), error) + newInstance(AlovaCompleteEvent, baseEvent, KEY_ERROR, undefinedValue, fromCache(), error) ); } diff --git a/packages/client/src/hooks/core/useFetcher.ts b/packages/client/src/hooks/core/useFetcher.ts index 78f72af2..dd7d85f0 100644 --- a/packages/client/src/hooks/core/useFetcher.ts +++ b/packages/client/src/hooks/core/useFetcher.ts @@ -14,6 +14,7 @@ export default function useFetcher>(config: FetcherHo Omit & { StatesExport: F['StatesExport']; }, + any[], FetcherHookConfig >(EnumHookType.USE_FETCHER, noop as any, config); const { send } = props; diff --git a/packages/client/src/hooks/core/useRequest.ts b/packages/client/src/hooks/core/useRequest.ts index 5cd7949f..0c559577 100644 --- a/packages/client/src/hooks/core/useRequest.ts +++ b/packages/client/src/hooks/core/useRequest.ts @@ -1,17 +1,17 @@ import { objAssign } from '@alova/shared/function'; import { trueValue } from '@alova/shared/vars'; import { AlovaGenerics, Method } from 'alova'; -import { AlovaMethodHandler, EnumHookType, RequestHookConfig } from '~/typings/clienthook'; +import { AlovaMethodHandler, EnumHookType, RequestHookConfig, UseHookExposure } from '~/typings/clienthook'; import createRequestState from './implements/createRequestState'; -export default function useRequest( - handler: Method | AlovaMethodHandler, - config: RequestHookConfig = {} +export default function useRequest( + handler: Method | AlovaMethodHandler, + config: RequestHookConfig = {} ) { const { immediate = trueValue, initialData } = config; const props = createRequestState(EnumHookType.USE_REQUEST, handler, config, initialData, !!immediate); const { send } = props; return objAssign(props, { - send: (...args: any[]) => send(args) - }); + send: (...args: [...Args, ...any[]]) => send(args) + }) as unknown as UseHookExposure; } diff --git a/packages/client/src/hooks/core/useWatcher.ts b/packages/client/src/hooks/core/useWatcher.ts index 698da3d7..85c2dd80 100644 --- a/packages/client/src/hooks/core/useWatcher.ts +++ b/packages/client/src/hooks/core/useWatcher.ts @@ -5,10 +5,10 @@ import { AlovaMethodHandler, EnumHookType, UseHookExposure, WatcherHookConfig } import { watcherHookAssert } from './implements/assert'; import createRequestState from './implements/createRequestState'; -export default function useWatcher( - handler: Method | AlovaMethodHandler, +export default function useWatcher( + handler: Method | AlovaMethodHandler, watchingStates: AG['StatesExport']['Watched'][], - config: WatcherHookConfig = {} + config: WatcherHookConfig = {} ) { watcherHookAssert(watchingStates && len(watchingStates) > 0, 'expected at least one watching state'); const { immediate, debounce = 0, initialData } = config; @@ -23,6 +23,6 @@ export default function useWatcher( ); const { send } = props; return objAssign(props, { - send: (...args: any[]) => send(args) - }) as UseHookExposure; + send: (...args: [...Args, ...any[]]) => send(args) + }) as unknown as UseHookExposure; } diff --git a/packages/client/src/hooks/pagination/usePagination.ts b/packages/client/src/hooks/pagination/usePagination.ts index 9de25503..19455520 100644 --- a/packages/client/src/hooks/pagination/usePagination.ts +++ b/packages/client/src/hooks/pagination/usePagination.ts @@ -9,7 +9,8 @@ import { isFn, isNumber, noop, - statesHookHelper + statesHookHelper, + usePromise } from '@alova/shared/function'; import { GeneralFn } from '@alova/shared/types'; import { @@ -25,7 +26,6 @@ import { objectValues, promiseCatch, promiseResolve, - promiseThen, pushItem, splice, trueValue, @@ -93,7 +93,7 @@ export default ( const { loading, fetch, abort: abortFetch, onSuccess: onFetchSuccess } = fetchStates; const fetchingRef = ref(loading); - const getHandlerMethod = (refreshPage = page.v) => { + const getHandlerMethod = (refreshPage: number | undefined = page.v) => { const pageSizeVal = pageSize.v; const handlerMethod = handler(refreshPage, pageSizeVal); @@ -123,44 +123,48 @@ export default ( (actionName: string) => (...args: any[]) => delegationActions.current[actionName](...args); - const states = useWatcher(getHandlerMethod, [...watchingStates, page.e, pageSize.e] as any, { - __referingObj: referingObject, - immediate, - initialData, - managedStates: objectify([data, page, pageSize, total], 's'), - middleware(ctx, next) { - (middleware as any)( - { - ...ctx, - delegatingActions: { - refresh: createDelegationAction('refresh'), - insert: createDelegationAction('insert'), - remove: createDelegationAction('remove'), - replace: createDelegationAction('replace'), - reload: createDelegationAction('reload'), - getState: (stateKey: string) => { - type SE = AG['StatesExport']; - const states: Record = { - page, - pageSize, - data, - pageCount, - total, - // eslint-disable-next-line @typescript-eslint/no-use-before-define - isLastPage - } as Record; - return states[stateKey].v; + const states = useWatcher( + getHandlerMethod, + [...watchingStates, page.e, pageSize.e] as any, + { + __referingObj: referingObject, + immediate, + initialData, + managedStates: objectify([data, page, pageSize, total], 's'), + middleware(ctx, next) { + middleware( + { + ...ctx, + delegatingActions: { + refresh: createDelegationAction('refresh'), + insert: createDelegationAction('insert'), + remove: createDelegationAction('remove'), + replace: createDelegationAction('replace'), + reload: createDelegationAction('reload'), + getState: (stateKey: string) => { + type SE = AG['StatesExport']; + const states: Record = { + page, + pageSize, + data, + pageCount, + total, + // eslint-disable-next-line @typescript-eslint/no-use-before-define + isLastPage + } as Record; + return states[stateKey].v; + } } - } - }, - promiseResolve - ); - - return next(); - }, - force: event => event.args[1] || (isFn(force) ? force(event) : force), - ...others - }); + }, + promiseResolve + ); + + return next(); + }, + force: event => event.args[1] || (isFn(force) ? (force(event) as boolean) : force), + ...others + } + ); const { send } = states; const nestedData = states.__proxyState('data'); @@ -291,42 +295,54 @@ export default ( } } }); - states.onSuccess(({ data: rawData, args: [refreshPage, isRefresh], method }) => { - const { total: cachedTotal } = getSnapshotMethods(method) || {}; - const typedRawData = rawData as any[]; - total.v = cachedTotal !== undefinedValue ? cachedTotal : totalGetter(typedRawData); - if (!isRefresh) { - fetchPreviousPage(typedRawData); - fetchNextPage(typedRawData); - } - - const pageSizeVal = pageSize.v; - const listData = listDataGetter(typedRawData); // 获取数组 - paginationAssert(isArray(listData), 'Got wrong array, did you return the correct array of list in `data` function'); - // 如果追加数据,才更新data - if (append) { - // 如果是reset则先清空数据 - if (isReset.current) { - data.v = []; - } - if (refreshPage === undefinedValue) { - data.v = [...data.v, ...listData]; - } else if (refreshPage) { - const rawData = [...data.v]; - // 如果是刷新页面,则是替换那一页的数据 - splice(rawData, (refreshPage - 1) * pageSizeVal, pageSizeVal, ...listData); - data.v = rawData; + const awaitResolve = ref(undefinedValue as GeneralFn | undefined); + const awaitReject = ref(undefinedValue as GeneralFn | undefined); + states + .onSuccess(({ data: rawData, args: [refreshPage, isRefresh], method }) => { + const { total: cachedTotal } = getSnapshotMethods(method) || {}; + const typedRawData = rawData as any[]; + total.v = cachedTotal !== undefinedValue ? cachedTotal : totalGetter(typedRawData); + if (!isRefresh) { + fetchPreviousPage(typedRawData); + fetchNextPage(typedRawData); } - } else { - data.v = listData; - } - }); - // 请求成功与否,都要重置isReset - states.onComplete(() => { - isReset.current = falseValue; - }); + const pageSizeVal = pageSize.v; + const listData = listDataGetter(typedRawData); // 获取数组 + paginationAssert( + isArray(listData), + 'Got wrong array, did you return the correct array of list in `data` function' + ); + + // 如果追加数据,才更新data + if (append) { + // 如果是reset则先清空数据 + if (isReset.current) { + data.v = []; + } + if (refreshPage === undefinedValue) { + data.v = [...data.v, ...listData]; + } else if (refreshPage) { + const rawData = [...data.v]; + // 如果是刷新页面,则是替换那一页的数据 + splice(rawData, (refreshPage - 1) * pageSizeVal, pageSizeVal, ...listData); + data.v = rawData; + } + } else { + data.v = listData; + } + }) + .onSuccess(({ data }) => { + awaitResolve.current?.(data); + }) + .onError(({ error }) => { + awaitReject.current?.(error); + }) + .onComplete(() => { + // 请求成功与否,都要重置isReset + isReset.current = falseValue; + }); // 获取列表项所在位置 const getItemIndex = (item: ListData[number]) => { @@ -343,8 +359,9 @@ export default ( * 如果传入一个列表项,将会刷新此列表项所在页,只对append模式有效 * @param pageOrItemPage 刷新的页码或列表项 */ - const refresh = (pageOrItemPage: number | ListData[number] = page.v) => { + const refresh = async (pageOrItemPage: number | ListData[number] = page.v) => { let refreshPage = pageOrItemPage as number; + let awaitPromise = promiseResolve(); if (append) { if (!isNumber(pageOrItemPage)) { const itemIndex = getItemIndex(pageOrItemPage); @@ -352,17 +369,16 @@ export default ( } paginationAssert(refreshPage <= page.v, "refresh page can't greater than page"); // 更新当前页数据 - promiseCatch(send(refreshPage, trueValue), noop); + awaitPromise = send(refreshPage, trueValue); } else { paginationAssert(isNumber(refreshPage), 'unable to calculate refresh page by item in pagination mode'); // 页数相等,则刷新当前页,否则fetch数据 - promiseCatch( + awaitPromise = refreshPage === page.v ? send(undefinedValue, trueValue) - : fetch(handler(refreshPage, pageSize.v) as Method, trueValue), - noop - ); + : fetch(handler(refreshPage, pageSize.v) as Method, trueValue); } + return awaitPromise; }; // 删除除此usehook当前页和下一页的所有相关缓存 @@ -545,11 +561,14 @@ export default ( /** * 从第${initialPage}页开始重新加载列表,并清空缓存 */ - const reload = () => { - promiseThen(invalidatePaginationCache(trueValue), () => { - isReset.current = trueValue; - page.v === initialPage ? promiseCatch(send(), noop) : (page.v = initialPage); - }); + const reload = async () => { + await invalidatePaginationCache(trueValue); + isReset.current = trueValue; + page.v === initialPage ? promiseCatch(send(), noop) : (page.v = initialPage); + const { resolve, reject, promise } = usePromise(); + awaitResolve.current = resolve; + awaitReject.current = reject; + return promise; }; // 兼容react,每次缓存最新的操作函数,避免闭包陷阱 diff --git a/packages/client/src/hooks/serial/general.ts b/packages/client/src/hooks/serial/general.ts index 9ebda088..f84d47be 100644 --- a/packages/client/src/hooks/serial/general.ts +++ b/packages/client/src/hooks/serial/general.ts @@ -23,8 +23,8 @@ export const assertSerialHandlers = (hookName: string, serialHandlers: any) => 'please use an array to represent serial requests' ); -export type SerialHandlers = [ - Method | AlovaMethodHandler, +export type SerialHandlers = [ + Method | AlovaMethodHandler, ...AlovaMethodHandler[] ]; @@ -34,9 +34,9 @@ export type SerialHandlers = [ * @param hookMiddleware use hook的中间件 * @returns 串行请求中间件 */ -export const serialMiddleware = ( - serialHandlers: SerialHandlers, - hookMiddleware?: AlovaFrontMiddleware, +export const serialMiddleware = ( + serialHandlers: SerialHandlers, + hookMiddleware?: AlovaFrontMiddleware, serialRequestMethods: Method[] = [] ) => { // 第一个handler在外部传递给了use hook,不需要再次请求 @@ -59,5 +59,5 @@ export const serialMiddleware = ( return serialPromise.finally(() => { loadingState.v = falseValue; }); - }) as AlovaFrontMiddleware; + }) as AlovaFrontMiddleware; }; diff --git a/packages/client/src/hooks/serial/useSerialRequest.ts b/packages/client/src/hooks/serial/useSerialRequest.ts index f8d06ef0..552fcbd7 100644 --- a/packages/client/src/hooks/serial/useSerialRequest.ts +++ b/packages/client/src/hooks/serial/useSerialRequest.ts @@ -13,15 +13,15 @@ import { assertSerialHandlers, serialMiddleware } from './general'; * @param config 配置参数 * @return useSerialRequest相关数据和操作函数 */ -export default ( - serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], - config: RequestHookConfig = {} as any +export default ( + serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], + config: RequestHookConfig = {} as any ) => { assertSerialHandlers('useSerialRequest', serialHandlers); // eslint-disable-next-line const { ref, __referingObj } = statesHookHelper(promiseStatesHook()); const methods = ref[]>([]).current; - const exposures = useRequest(serialHandlers[0], { + const exposures = useRequest(serialHandlers[0], { ...config, __referingObj, middleware: serialMiddleware(serialHandlers, config.middleware, methods) diff --git a/packages/client/src/hooks/serial/useSerialWatcher.ts b/packages/client/src/hooks/serial/useSerialWatcher.ts index 22775cf0..657bc484 100644 --- a/packages/client/src/hooks/serial/useSerialWatcher.ts +++ b/packages/client/src/hooks/serial/useSerialWatcher.ts @@ -13,16 +13,16 @@ import { assertSerialHandlers, serialMiddleware } from './general'; * @param config 配置参数 * @return useSerialRequest相关数据和操作函数 */ -export default ( - serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], +export default ( + serialHandlers: [Method | AlovaMethodHandler, ...AlovaMethodHandler[]], watchingStates: AG['StatesExport']['Watched'][], - config: RequestHookConfig = {} as any + config: RequestHookConfig = {} as any ) => { assertSerialHandlers('useSerialWatcher', serialHandlers); // eslint-disable-next-line const { ref, __referingObj } = statesHookHelper(promiseStatesHook()); const methods = ref[]>([]).current; - const exposures = useWatcher(serialHandlers[0], watchingStates, { + const exposures = useWatcher(serialHandlers[0], watchingStates, { ...config, __referingObj, middleware: serialMiddleware(serialHandlers, config.middleware, methods) diff --git a/packages/client/src/hooks/silent/createSilentQueueMiddlewares.ts b/packages/client/src/hooks/silent/createSilentQueueMiddlewares.ts index 0139bfbb..e8726fe6 100644 --- a/packages/client/src/hooks/silent/createSilentQueueMiddlewares.ts +++ b/packages/client/src/hooks/silent/createSilentQueueMiddlewares.ts @@ -52,7 +52,10 @@ export let currentSilentMethod: SilentMethod | undefined = undefinedValue; * @param config 配置对象 * @returns 中间件函数 */ -export default (handler: Method | AlovaMethodHandler, config?: SQHookConfig) => { +export default ( + handler: Method | AlovaMethodHandler, + config?: SQHookConfig +) => { const { behavior = 'queue', queue = DEFAULT_QUEUE_NAME, retryError, maxRetryTimes, backoff } = config || {}; const eventEmitter = createEventManager>(); let handlerArgs: any[] | undefined; @@ -74,13 +77,13 @@ export default (handler: Method | AlovaMethodHandl }; // 装饰success/error/complete事件 - const decorateRequestEvent = (requestExposure: UseHookExposure) => { + const decorateRequestEvent = (requestExposure: UseHookExposure) => { // 设置事件回调装饰器 requestExposure.onSuccess = decorateEvent(requestExposure.onSuccess, (handler, event) => { currentSilentMethod = silentMethodInstance; handler( newInstance( - ScopedSQSuccessEvent, + ScopedSQSuccessEvent, behaviorFinally, event.method, silentMethodInstance, @@ -93,7 +96,7 @@ export default (handler: Method | AlovaMethodHandl requestExposure.onError = decorateEvent(requestExposure.onError, (handler, event) => { handler( newInstance( - ScopedSQErrorEvent, + ScopedSQErrorEvent, behaviorFinally, event.method, silentMethodInstance, @@ -105,7 +108,7 @@ export default (handler: Method | AlovaMethodHandl requestExposure.onComplete = decorateEvent(requestExposure.onComplete, (handler, event) => { handler( newInstance( - ScopedSQCompleteEvent, + ScopedSQCompleteEvent, behaviorFinally, event.method, silentMethodInstance, @@ -124,7 +127,7 @@ export default (handler: Method | AlovaMethodHandl * @param next 继续执行函数 * @returns Promise对象 */ - const middleware: AlovaFrontMiddleware = ({ method, args, cachedResponse, proxyStates, config }, next) => { + const middleware: AlovaFrontMiddleware = ({ method, args, cachedResponse, proxyStates, config }, next) => { const { silentDefaultResponse, vDataCaptured, force = falseValue } = config; // 因为behavior返回值可能会变化,因此每次请求都应该调用它重新获取返回值 @@ -186,7 +189,7 @@ export default (handler: Method | AlovaMethodHandl // onBeforePush和onPushed事件是同步绑定的,因此需要异步执行入队列才能正常触发事件 promiseThen(promiseResolve(undefinedValue), async () => { const createPushEvent = () => - newInstance(ScopedSQEvent, behaviorFinally, method, silentMethodInstance, args); + newInstance(ScopedSQEvent, behaviorFinally, method, silentMethodInstance, args); // 将silentMethod放入队列并持久化 const isPushed = await pushNewSilentMethod2Queue( diff --git a/packages/client/src/hooks/silent/silentQueue.ts b/packages/client/src/hooks/silent/silentQueue.ts index 594e2abe..6f510df4 100644 --- a/packages/client/src/hooks/silent/silentQueue.ts +++ b/packages/client/src/hooks/silent/silentQueue.ts @@ -337,7 +337,7 @@ export const bootSilentQueue = (queue: SilentQueueMap[string], queueName: string // 达到失败次数,或不匹配重试的错误信息时,触发失败回调 methodEmitter.emit( 'fallback', - newInstance(ScopedSQErrorEvent, behavior, entity, silentMethodInstance, handlerArgs, reason) + newInstance(ScopedSQErrorEvent, behavior, entity, silentMethodInstance, handlerArgs, reason) ); globalSQEventManager.emit( FailEventKey, diff --git a/packages/client/src/hooks/silent/useSQRequest.ts b/packages/client/src/hooks/silent/useSQRequest.ts index 27d7e571..8779795a 100644 --- a/packages/client/src/hooks/silent/useSQRequest.ts +++ b/packages/client/src/hooks/silent/useSQRequest.ts @@ -4,9 +4,9 @@ import { AlovaGenerics, promiseStatesHook } from 'alova'; import { AlovaMethodHandler, SQRequestHookConfig, UseHookExposure } from '~/typings/clienthook'; import createSilentQueueMiddlewares from './createSilentQueueMiddlewares'; -export default function useSQRequest( - handler: AlovaMethodHandler, - config: SQRequestHookConfig = {} +export default function useSQRequest( + handler: AlovaMethodHandler, + config: SQRequestHookConfig = {} ) { const { exposeProvider, __referingObj: referingObj } = statesHookHelper(promiseStatesHook()); const { middleware = noop } = config; @@ -25,7 +25,7 @@ export default function useSQRequest( return silentMidPromise; } }); - decorateEvent(states as UseHookExposure); + decorateEvent(states as unknown as UseHookExposure); return exposeProvider({ ...states, diff --git a/packages/client/src/hooks/useAutoRequest.ts b/packages/client/src/hooks/useAutoRequest.ts index 62040bcd..b9426134 100644 --- a/packages/client/src/hooks/useAutoRequest.ts +++ b/packages/client/src/hooks/useAutoRequest.ts @@ -11,10 +11,10 @@ import { } from '~/typings/clienthook'; interface AutoRequestHook { - ( - handler: Method | AlovaMethodHandler, + ( + handler: Method | AlovaMethodHandler, config?: AutoRequestHookConfig - ): UseHookExposure; + ): UseHookExposure; onNetwork( notify: NotifyHandler, config: AutoRequestHookConfig @@ -54,7 +54,7 @@ const useAutoRequest: AutoRequestHook = (handler, config = {}) => { }); const notify = () => { if (notifiable) { - states.send(); + (states.send as any)(); if (throttle > 0) { notifiable = falseValue; setTimeout(() => { diff --git a/packages/client/src/hooks/useCaptcha.ts b/packages/client/src/hooks/useCaptcha.ts index afdb1999..94add27e 100644 --- a/packages/client/src/hooks/useCaptcha.ts +++ b/packages/client/src/hooks/useCaptcha.ts @@ -7,8 +7,8 @@ import { AlovaMethodHandler, CaptchaHookConfig } from '~/typings/clienthook'; const hookPrefix = 'useCaptcha'; const captchaAssert = createAssert(hookPrefix); -export default ( - handler: Method | AlovaMethodHandler, +export default ( + handler: Method | AlovaMethodHandler, config: CaptchaHookConfig = {} ) => { const { initialCountdown, middleware } = config; @@ -32,7 +32,7 @@ export default ( }); const timer = ref(undefinedValue as NodeJS.Timeout | undefined); - const send = (...args: any[]) => + const send = (...args: [...Args, ...any[]]) => newInstance(PromiseCls, (resolve, reject) => { if (countdown.v <= 0) { requestReturned diff --git a/packages/client/src/hooks/useForm.ts b/packages/client/src/hooks/useForm.ts index d4984315..3a3efcf8 100644 --- a/packages/client/src/hooks/useForm.ts +++ b/packages/client/src/hooks/useForm.ts @@ -25,14 +25,14 @@ const cloneFormData = (form: T): T => { return walkObject(shallowClone(form), shallowClone); }; -export default >( - handler: FormHookHandler, +export default , Args extends any[] = any[]>( + handler: FormHookHandler, config: FormHookConfig = {} -): FormExposure => { +): FormExposure => { const typedSharedStates = sharedStates as Record< ID, { - hookProvider: FormExposure; + hookProvider: FormExposure; config: FormHookConfig; } >; @@ -69,7 +69,7 @@ export default methodHandler(form.v as FormData, ...args), { + const originalHookProvider = useRequest((...args: Args) => methodHandler(form.v as FormData, ...args), { ...config, __referingObj: referingObject, @@ -154,7 +154,7 @@ export default = { - [RetryEventKey]: RetriableRetryEvent; - [FailEventKey]: RetriableFailEvent; +export type RetriableEvents = { + [RetryEventKey]: RetriableRetryEvent; + [FailEventKey]: RetriableFailEvent; }; -type RetryHandler = (event: RetriableRetryEvent) => void; -type FailHandler = (event: RetriableFailEvent) => void; +type RetryHandler = (event: RetriableRetryEvent) => void; +type FailHandler = (event: RetriableFailEvent) => void; const hookPrefix = 'useRetriableRequest'; const assert = createAssert(hookPrefix); -export default ( - handler: Method | AlovaMethodHandler, - config: RetriableHookConfig = {} +export default ( + handler: Method | AlovaMethodHandler, + config: RetriableHookConfig = {} ) => { const { retry = 3, backoff = { delay: 1000 }, middleware = noop } = config; const { ref: useFlag$, exposeProvider, __referingObj: referingObject } = statesHookHelper(promiseStatesHook()); - const eventManager = createEventManager>(); + const eventManager = createEventManager>(); const retryTimes = useFlag$(0); const stopManuallyError = useFlag$(undefinedValue as Error | undefined); // 停止错误对象,在手动触发停止时有值 const methodInstanceLastest = useFlag$(undefinedValue as Method | undefined); @@ -48,7 +48,7 @@ export default ( const promiseObj = useFlag$(usePromise()); const requestResolved = useFlag$(falseValue); - const emitOnFail = (method: Method, args: any[], error: any) => { + const emitOnFail = (method: Method, args: [...Args, ...any[]], error: any) => { if (requestResolved.current) { return; } @@ -57,7 +57,7 @@ export default ( setTimeoutFn(() => { eventManager.emit( FailEventKey, - newInstance(RetriableFailEvent, AlovaEventBase.spawn(method, args), error, retryTimes.current) + newInstance(RetriableFailEvent, AlovaEventBase.spawn(method, args), error, retryTimes.current) ); stopManuallyError.current = undefinedValue; retryTimes.current = 0; // 重置已重试次数 @@ -76,7 +76,7 @@ export default ( stop } } as any, - () => promiseResolve(undefinedValue as any) + () => promiseResolve() ); const { proxyStates, args, send, method, controlLoading } = ctx; const setLoading = (loading = falseValue) => { @@ -122,11 +122,16 @@ export default ( // 延迟对应时间重试 retryTimer.current = setTimeoutFn(() => { // 如果手动停止了则不再触发重试 - promiseCatch(send(...args), noop); // 捕获错误不再往外抛,否则重试时也会抛出错误 + promiseCatch(send(...(args as any)), noop); // 捕获错误不再往外抛,否则重试时也会抛出错误 // 触发重试事件 eventManager.emit( RetryEventKey, - newInstance(RetriableRetryEvent, AlovaEventBase.spawn(method, args), retryTimes.current, retryDelay) + newInstance( + RetriableRetryEvent, + AlovaEventBase.spawn(method, args), + retryTimes.current, + retryDelay + ) ); }, retryDelay); } else { @@ -158,7 +163,7 @@ export default ( setTimeout(() => { promiseObj.current = usePromise(); }); - nestedHookProvider.update({ error: stopManuallyError.current, loading: falseValue }); + nestedHookProvider.update({ error: stopManuallyError.current as any, loading: falseValue as any }); currentLoadingState.current = falseValue; clearTimeout(retryTimer.current); // 清除重试定时器 @@ -173,7 +178,7 @@ export default ( * 它们将在重试发起后触发 * @param handler 重试事件回调 */ - const onRetry = (handler: RetryHandler) => { + const onRetry = (handler: RetryHandler) => { eventManager.on(RetryEventKey, event => handler(event)); }; @@ -186,7 +191,7 @@ export default ( * * @param handler 失败事件回调 */ - const onFail = (handler: FailHandler) => { + const onFail = (handler: FailHandler) => { eventManager.on(FailEventKey, event => handler(event)); }; diff --git a/packages/client/src/hooks/useSSE.ts b/packages/client/src/hooks/useSSE.ts index 83a5be3f..b679346b 100644 --- a/packages/client/src/hooks/useSSE.ts +++ b/packages/client/src/hooks/useSSE.ts @@ -32,15 +32,15 @@ const SSEOpenEventKey = Symbol('SSEOpen'); const SSEMessageEventKey = Symbol('SSEMessage'); const SSEErrorEventKey = Symbol('SSEError'); -export type SSEEvents = { - [SSEOpenEventKey]: AlovaSSEEvent; - [SSEMessageEventKey]: AlovaSSEMessageEvent; - [SSEErrorEventKey]: AlovaSSEErrorEvent; +export type SSEEvents = { + [SSEOpenEventKey]: AlovaSSEEvent; + [SSEMessageEventKey]: AlovaSSEMessageEvent; + [SSEErrorEventKey]: AlovaSSEErrorEvent; }; -type AnySSEEventType = AlovaSSEMessageEvent & - AlovaSSEErrorEvent & - AlovaSSEEvent; +type AnySSEEventType = AlovaSSEMessageEvent & + AlovaSSEErrorEvent & + AlovaSSEEvent; const assert = createAssert('useSSE'); const MessageType: Record, keyof EventSourceEventMap> = { @@ -49,8 +49,8 @@ const MessageType: Record, keyof EventSour Message: 'message' } as const; -export default ( - handler: Method | AlovaMethodHandler, +export default ( + handler: Method | AlovaMethodHandler, config: SSEHookConfig = {} ) => { const { @@ -68,7 +68,7 @@ export default ( const { create, ref, onMounted, onUnmounted, objectify, exposeProvider } = statesHookHelper(promiseStatesHook()); - const usingArgs = ref([]); + const usingArgs = ref<[...Args, ...any[]]>([] as any); const eventSource = ref(undefinedValue); const sendPromiseObject = ref | undefined>(undefinedValue); @@ -79,16 +79,16 @@ export default ( let responseUnified: RespondedHandler | RespondedHandlerRecord | undefined; - const eventManager = createEventManager>(); + const eventManager = createEventManager>(); // 储存自定义事件的 useCallback 对象,其中 key 为 eventName const customEventMap = ref(new Map>()); - const onOpen = (handler: (event: AlovaSSEEvent) => void) => { + const onOpen = (handler: (event: AlovaSSEEvent) => void) => { eventManager.on(SSEOpenEventKey, handler); }; - const onMessage = (handler: (event: AlovaSSEMessageEvent) => void) => { + const onMessage = (handler: (event: AlovaSSEMessageEvent) => void) => { eventManager.on(SSEMessageEventKey, handler); }; - const onError = (handler: (event: AlovaSSEErrorEvent) => void) => { + const onError = (handler: (event: AlovaSSEErrorEvent) => void) => { eventManager.on(SSEErrorEventKey, handler); }; @@ -142,7 +142,10 @@ export default ( assert(!!eventSource.current, 'EventSource is not initialized'); const es = eventSource.current!; - const baseEvent = new AlovaSSEEvent(AlovaEventBase.spawn(methodInstance, usingArgs.current), es); + const baseEvent = new AlovaSSEEvent( + AlovaEventBase.spawn(methodInstance, usingArgs.current), + es + ); if (eventFrom === MessageType.Open) { return Promise.resolve(baseEvent); @@ -168,7 +171,7 @@ export default ( return promiseThen( p, // 得到处理好的数据(transform 之后的数据) - res => new AlovaSSEMessageEvent(baseEvent, res), + res => new AlovaSSEMessageEvent(baseEvent, res), // 有错误 error => new AlovaSSEErrorEvent(baseEvent, error) ); @@ -178,19 +181,20 @@ export default ( * 根据事件选择需要的触发函数。如果事件无错误则触发传传入的回调函数 * @param callback 无错误时触发的回调函数 */ - const sendSSEEvent = (callback: (event: AnySSEEventType) => any) => (event: AnySSEEventType) => { - if (event.error === undefinedValue) { - return callback(event); - } - return eventManager.emit(SSEErrorEventKey, event); - }; + const sendSSEEvent = + (callback: (event: AnySSEEventType) => any) => (event: AnySSEEventType) => { + if (event.error === undefinedValue) { + return callback(event); + } + return eventManager.emit(SSEErrorEventKey, event); + }; // * MARK: EventSource 的事件处理 - const onCustomEvent: SSEOn = (eventName, callbackHandler) => { + const onCustomEvent: SSEOn = (eventName, callbackHandler) => { const currentMap = customEventMap.current; if (!currentMap.has(eventName)) { - const useCallbackObject = useCallback<(event: AlovaSSEEvent) => void>(callbacks => { + const useCallbackObject = useCallback<(event: AlovaSSEEvent) => void>(callbacks => { if (callbacks.length === 0) { eventSource.current?.removeEventListener(eventName, useCallbackObject[1] as any); customEventMap.current.delete(eventName); @@ -274,7 +278,7 @@ export default ( /** * 发送请求并初始化 eventSource */ - const connect = (...args: any[]) => { + const connect = (...args: [...Args, ...any[]]) => { let es = eventSource.current; let promiseObj = sendPromiseObject.current; if (es && abortLast) { @@ -337,7 +341,7 @@ export default ( // * MARK: 初始化动作 onMounted(() => { if (immediate) { - connect(); + connect(...([] as unknown as [...Args, ...any[]])); sendPromiseObject.current?.promise.catch(() => {}); } }); diff --git a/packages/client/src/middlewares/actionDelegation.ts b/packages/client/src/middlewares/actionDelegation.ts index 9972a1a6..552e32b9 100644 --- a/packages/client/src/middlewares/actionDelegation.ts +++ b/packages/client/src/middlewares/actionDelegation.ts @@ -10,9 +10,9 @@ import { } from '~/typings/clienthook'; const actionsMap: Record = {}; -const isFrontMiddlewareContext = ( - context: AlovaFrontMiddlewareContext | AlovaFetcherMiddlewareContext -): context is AlovaFrontMiddlewareContext => !!(context as AlovaFrontMiddlewareContext).send; +const isFrontMiddlewareContext = ( + context: AlovaFrontMiddlewareContext | AlovaFetcherMiddlewareContext +): context is AlovaFrontMiddlewareContext => !!(context as AlovaFrontMiddlewareContext).send; const assert = createAssert('subscriber'); @@ -24,13 +24,17 @@ const assert = createAssert('subscriber'); * @param id 委托者id * @returns alova中间件函数 */ -export const actionDelegationMiddleware = (id: string | number | symbol) => { +export const actionDelegationMiddleware = ( + id: string | number | symbol +) => { const { ref } = statesHookHelper(promiseStatesHook()); const delegated = ref(falseValue); return ( - context: (AlovaFrontMiddlewareContext | AlovaFetcherMiddlewareContext) & { delegatingActions?: Actions }, - next: AlovaGuardNext + context: (AlovaFrontMiddlewareContext | AlovaFetcherMiddlewareContext) & { + delegatingActions?: Actions; + }, + next: AlovaGuardNext ) => { // 中间件会重复调用,已经订阅过了就无需再订阅了 if (!delegated.current) { diff --git a/packages/client/src/statesHook/react.ts b/packages/client/src/statesHook/react.ts index 0710e976..edc59610 100644 --- a/packages/client/src/statesHook/react.ts +++ b/packages/client/src/statesHook/react.ts @@ -10,7 +10,7 @@ const setRef = (ref: MutableRefObject, newVal: T) => { ref.current = newVal; }; -// React的预定义hooks +// the react predefined hooks export default { name: 'React', create: initialValue => useState(initialValue), @@ -22,7 +22,7 @@ export default { state[1](newVal); }, memorize: fn => { - // 使用useCallback使用同一个引用,同事通过useRef来引用最新函数 + // use `useCallback` to ensure the same reference, and refer the newest function with `useRef` const fnRef = useRef(noop as typeof fn); setRef(fnRef, fn); return useCallback((...args: any[]) => refCurrent(fnRef)(...args), []); @@ -33,14 +33,14 @@ export default { return refObj; }, effectRequest({ handler, removeStates, saveStates, immediate, frontStates, watchingStates = [] }) { - // 当有监听状态时,状态变化再触发 + // `handler` is called when some states change are detected const oldStates = useRef(watchingStates); - // 多个值同时改变只触发一次 + // only call once when multiple values changed at the same time const onceRunner = refCurrent(useRef(createSyncOnceRunner())); useEffect(() => { const oldStatesValue = refCurrent(oldStates); - // 对比新旧状态,获取变化的状态索引 + // compare the old and new value, and get the index of changed state let changedIndex: number | undefined = undefinedValue; for (const index in watchingStates) { if (!Object.is(oldStatesValue[index], watchingStates[index])) { @@ -55,12 +55,12 @@ export default { } }); - // 组件卸载时移除对应状态 + // remove states when component is unmounted return removeStates; }, watchingStates); - // 因为react每次刷新都会重新调用usehook,因此每次会让状态缓存失效 - // 因此每次都需要更新管理的状态 + // Because react will call usehook again every time it refreshes, the state cache will be invalidated every time + // Therefore, the managed state needs to be updated every time const needSave = useRef(false); useEffect(() => { refCurrent(needSave) ? saveStates(frontStates) : setRef(needSave, trueValue); @@ -71,7 +71,7 @@ export default { return [memo, noop]; }, watch: (states, callback) => { - // 当有监听状态时,状态变化再触发 + // When there is a monitoring state, the state changes and then triggers const needEmit = useRef(falseValue); useEffect(() => { needEmit.current ? callback() : (needEmit.current = true); diff --git a/packages/client/src/statesHook/svelte.ts b/packages/client/src/statesHook/svelte.ts index 457db544..caf23d32 100644 --- a/packages/client/src/statesHook/svelte.ts +++ b/packages/client/src/statesHook/svelte.ts @@ -5,12 +5,13 @@ import { onDestroy, onMount } from 'svelte'; import { derived, writable } from 'svelte/store'; import { SvelteHookExportType } from '~/typings/stateshook/svelte'; +// the svelte predefined hooks export default { name: 'Svelte', create: data => writable(data), dehydrate: state => { let raw; - // 订阅时会立即执行一次函数,获取到值后立即调用解除订阅函数 + // The function will be executed once when subscribing, and the unsubscribe function will be called immediately after the value is obtained state.subscribe(value => { raw = value; })(); @@ -20,7 +21,7 @@ export default { state.set(newVal); }, effectRequest({ handler, removeStates, immediate, watchingStates }) { - // 组件卸载时移除对应状态 + // Remove the corresponding state when the component is unmounted onDestroy(removeStates); onMount(() => immediate && handler()); @@ -29,7 +30,7 @@ export default { forEach(watchingStates || [], (state, i) => { state.subscribe(() => { syncRunner(() => { - // svelte的writable默认会触发一次,因此当immediate为false时需要过滤掉第一次触发调用 + // Svelte's `writable` will trigger once by default, so when immediate is false, you need to filter out the first trigger call needEmit ? handler(i) : (needEmit = trueValue); }); }); diff --git a/packages/client/src/statesHook/vue.ts b/packages/client/src/statesHook/vue.ts index a380022c..3a48bcc7 100644 --- a/packages/client/src/statesHook/vue.ts +++ b/packages/client/src/statesHook/vue.ts @@ -4,7 +4,7 @@ import { StatesHook } from 'alova'; import { computed, getCurrentInstance, onMounted, onUnmounted, ref, watch } from 'vue'; import { VueHookExportType } from '~/typings/stateshook/vue'; -// Vue的预定义hooks +// the vue's predefined hooks export default { name: 'Vue', create: data => ref(data), @@ -13,12 +13,12 @@ export default { state.value = newVal; }, effectRequest({ handler, removeStates, immediate, watchingStates }) { - // 当在组件内部使用时,组件卸载时移除对应状态 + // if call in component, remove current hook states when unmounting component if (getCurrentInstance()) { onUnmounted(removeStates); onMounted(() => immediate && handler()); } else { - // 在非组件内部使用时,使用定时器延迟执行 + // if call outside component, run asynchronously with `Promise` setTimeoutFn(() => { immediate && handler(); }); diff --git a/packages/client/test/react/components/Pagination.tsx b/packages/client/test/react/components/Pagination.tsx index a7cd388d..09bf994f 100644 --- a/packages/client/test/react/components/Pagination.tsx +++ b/packages/client/test/react/components/Pagination.tsx @@ -44,13 +44,36 @@ function Pagination({ getter, paginationConfig = {}, handleExposure = () => {} } pageSize, isLastPage, update, - refresh, insert, replace, remove, - reload + refresh } = exposure; + const [awaitResult, setAwaitResult] = useState(); + const refreshWithCatch = async (...args: any[]) => { + setAwaitResult(undefined); + return exposure + .refresh(...args) + .then(() => { + setAwaitResult('resolve'); + }) + .catch(() => { + setAwaitResult('reject'); + }); + }; + const reloadWithCatch = async () => { + setAwaitResult(undefined); + return exposure + .reload() + .then(() => { + setAwaitResult('resolve'); + }) + .catch(() => { + setAwaitResult('reject'); + }); + }; + return (
{loading ? 'loading' : 'loaded'} @@ -62,6 +85,7 @@ function Pagination({ getter, paginationConfig = {}, handleExposure = () => {} } {JSON.stringify(data)} {error?.message} {replacedError?.message} + {awaitResult ? {awaitResult} : null}