Skip to content

Commit

Permalink
feat: alova@3.1.0 new features (#563)
Browse files Browse the repository at this point in the history
* 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 <capitaycity@gmail.com>
  • Loading branch information
JOU-amjs and MeetinaXD authored Oct 12, 2024
1 parent 8117fcb commit ea20f56
Show file tree
Hide file tree
Showing 81 changed files with 1,186 additions and 765 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-ducks-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'alova': minor
---

the function `refresh` and `reload` return by `usePagination` will return a promise
5 changes: 4 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
6 changes: 6 additions & 0 deletions .changeset/cuddly-falcons-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@alova/adapter-uniapp': patch
'alova': patch
---

loading value depends on `immdediate` event if `middleware` is set
6 changes: 6 additions & 0 deletions .changeset/empty-singers-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@alova/shared': patch
'alova': patch
---

feat: add inference for send arguments
5 changes: 5 additions & 0 deletions .changeset/silly-glasses-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'alova': patch
---

optimize the type `Method` to extend `Promise`
4 changes: 4 additions & 0 deletions internal/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,7 @@ export const generateContinuousNumbers = (

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const expectType = <T>(value: T) => {};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const expectTrue = <T extends true>() => {};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const expectAssignableBy = <T, T2 extends T = T>(value: T2) => {};
1 change: 0 additions & 1 deletion packages/adapter-uniapp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter-uniapp/src/statesHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter-uniapp/test/requestAdapter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});

Expand Down Expand Up @@ -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');
});
});
14 changes: 9 additions & 5 deletions packages/alova/src/Method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export default class Method<AG extends AlovaGenerics = any> {

public fromCache: boolean | undefined = undefinedValue;

[Symbol.toStringTag]: string;

constructor(
type: MethodType,
context: Alova<AG>,
Expand Down Expand Up @@ -191,9 +193,9 @@ export default class Method<AG extends AlovaGenerics = any> {
* @returns 返回一个Promise,用于执行任何回调
*/
public then<TResult1 = AG['Responded'], TResult2 = never>(
onfulfilled?: (value: AG['Responded']) => TResult1 | PromiseLike<TResult1>,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
) {
onfulfilled?: ((value: AG['Responded']) => TResult1 | PromiseLike<TResult1>) | null | undefined,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
): Promise<TResult1 | TResult2> {
return promiseThen(this.send(), onfulfilled, onrejected);
}

Expand All @@ -202,7 +204,9 @@ export default class Method<AG extends AlovaGenerics = any> {
* @param onrejected 当Promise被reject时要执行的回调
* @returns 返回一个完成回调的Promise
*/
public catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null) {
public catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined
): Promise<AG['Responded'] | TResult> {
return promiseCatch(this.send(), onrejected);
}

Expand All @@ -211,7 +215,7 @@ export default class Method<AG extends AlovaGenerics = any> {
* @param onfinally Promise结算(resolve或reject)时执行的回调。
* @return 返回一个完成回调的Promise。
*/
public finally(onfinally?: (() => void) | undefined | null) {
public finally(onfinally?: (() => void) | null | undefined): Promise<AG['Responded']> {
return promiseFinally(this.send(), onfinally);
}
}
139 changes: 80 additions & 59 deletions packages/alova/typings/clienthook/general.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ export interface StateMap<T> {
/**
* alova base event
*/
export interface AlovaEvent<AG extends AlovaGenerics> {
export interface AlovaEvent<AG extends AlovaGenerics, Args extends any[]> {
/**
* params from send function
*/
args: any[];
args: [...Args, ...any[]];
/**
* current method instance
*/
Expand All @@ -37,17 +37,17 @@ export interface AlovaEvent<AG extends AlovaGenerics> {
/**
* success event object
*/
export interface AlovaSuccessEvent<AG extends AlovaGenerics> extends AlovaEvent<AG> {
export interface AlovaSuccessEvent<AG extends AlovaGenerics, Args extends any[] = any[]> extends AlovaEvent<AG, Args> {
/** data数据是否来自缓存 */
fromCache: boolean;
data: AG['Responded'];
}
/** 错误事件对象 */
export interface AlovaErrorEvent<AG extends AlovaGenerics> extends AlovaEvent<AG> {
export interface AlovaErrorEvent<AG extends AlovaGenerics, Args extends any[]> extends AlovaEvent<AG, Args> {
error: any;
}
/** 完成事件对象 */
export interface AlovaCompleteEvent<AG extends AlovaGenerics> extends AlovaEvent<AG> {
export interface AlovaCompleteEvent<AG extends AlovaGenerics, Args extends any[]> extends AlovaEvent<AG, Args> {
/** 响应状态 */
status: 'success' | 'error';
/** data数据是否来自缓存,当status为error时,fromCache始终为false */
Expand All @@ -73,18 +73,22 @@ export type StateUpdater<ExportedStates extends Record<string, any>, SE extends
: ExportedStates[K];
}) => void;

export type AlovaMethodHandler<AG extends AlovaGenerics = any> = (...args: any[]) => Method<AG>;
export type SuccessHandler<AG extends AlovaGenerics> = (event: AlovaSuccessEvent<AG>) => void;
export type ErrorHandler<AG extends AlovaGenerics> = (event: AlovaErrorEvent<AG>) => void;
export type CompleteHandler<AG extends AlovaGenerics> = (event: AlovaCompleteEvent<AG>) => void;
export type AlovaMethodHandler<AG extends AlovaGenerics = any, Args extends any[] = any[]> = (
...args: Args
) => Method<AG>;
export type SuccessHandler<AG extends AlovaGenerics, Args extends any[]> = (event: AlovaSuccessEvent<AG, Args>) => void;
export type ErrorHandler<AG extends AlovaGenerics, Args extends any[]> = (event: AlovaErrorEvent<AG, Args>) => void;
export type CompleteHandler<AG extends AlovaGenerics, Args extends any[]> = (
event: AlovaCompleteEvent<AG, Args>
) => void;

/** common hook configuration */
export interface UseHookConfig<AG extends AlovaGenerics> {
export interface UseHookConfig<AG extends AlovaGenerics, Args extends any[] = any[]> {
/**
* force request or not
* force request
* @default false
*/
force?: boolean | ((event: AlovaEvent<AG>) => boolean);
force?: boolean | ((event: AlovaEvent<AG, Args>) => boolean);

/**
* refering object that sharing some value with this object.
Expand All @@ -98,36 +102,39 @@ export interface UseHookConfig<AG extends AlovaGenerics> {
}

export interface AlovaMiddlewareContext<AG extends AlovaGenerics> {
/** 当前的method对象 */
/** current method instance */
method: Method<AG>;

/** 命中的缓存数据 */
/**
* 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<AG extends AlovaGenerics> {
force?: UseHookConfig<AG>['force'];
/** next function of middleware */
export interface MiddlewareNextGuardConfig<AG extends AlovaGenerics, Args extends any[]> {
force?: UseHookConfig<AG, Args>['force'];
method?: Method<AG>;
}

/**
* useRequest和useWatcher中间件的context参数
*/
export interface AlovaFrontMiddlewareContext<AG extends AlovaGenerics> extends AlovaMiddlewareContext<AG> {
/** 发送请求函数 */
send: SendHandler<AG['Responded']>;
export interface AlovaFrontMiddlewareContext<AG extends AlovaGenerics, Args extends any[] = any[]>
extends AlovaMiddlewareContext<AG> {
/** handler to send request */
send: SendHandler<Args, AG['Responded']>;

/** args 响应处理回调的参数,该参数由use hooks的send传入 */
args: any[];
args: [...Args, ...any[]];

/** 前端状态集合 */
/** state proxies set */
proxyStates: FrontRequestState<
FrameworkState<boolean, 'loading'>,
FrameworkState<AG['Responded'], 'data'>,
Expand All @@ -137,32 +144,38 @@ export interface AlovaFrontMiddlewareContext<AG extends AlovaGenerics> 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<AG extends AlovaGenerics> {
(context: AlovaFrontMiddlewareContext<AG>, next: AlovaGuardNext<AG>): any;
export interface AlovaFrontMiddleware<AG extends AlovaGenerics, Args extends any[] = any[]> {
(context: AlovaFrontMiddlewareContext<AG, Args>, next: AlovaGuardNext<AG, Args>): any;
}

/**
* useFetcher中间件的context参数
* the context param of middleware in useFetcher
*/
export interface AlovaFetcherMiddlewareContext<AG extends AlovaGenerics> extends AlovaMiddlewareContext<AG> {
/** 数据预加载函数 */
fetch<Transformed>(method: Method<AG>, ...args: any[]): Promise<Transformed>;
export interface AlovaFetcherMiddlewareContext<AG extends AlovaGenerics, Args extends any[]>
extends AlovaMiddlewareContext<AG> {
/** fetch data */
fetch<Transformed>(method: Method<AG>, ...args: [...Args, ...any[]]): Promise<Transformed>;

/** args 响应处理回调的参数,该参数由useFetcher的fetch传入 */
args: any[];
args: [...Args, ...any[]];

/** fetch状态的代理集合 */
/** state proxies set */
proxyStates: FetchRequestState<
FrameworkState<boolean, 'loading'>,
FrameworkState<Error | undefined, 'error'>,
Expand All @@ -171,19 +184,24 @@ export interface AlovaFetcherMiddlewareContext<AG extends AlovaGenerics> 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<AG extends AlovaGenerics> {
(context: AlovaFetcherMiddlewareContext<AG>, next: AlovaGuardNext<AG>): any;
export interface AlovaFetcherMiddleware<AG extends AlovaGenerics, Args extends any[] = any[]> {
(context: AlovaFetcherMiddlewareContext<AG, Args>, next: AlovaGuardNext<AG, Args>): any;
}

export type ProxyStateGetter<HookExportedStates extends Record<string, any>> = <K extends keyof HookExportedStates>(
Expand All @@ -194,11 +212,11 @@ export type ProxyStateGetter<HookExportedStates extends Record<string, any>> = <
? FrameworkReadableState<Data, K & string>
: never;

export interface AlovaGuardNext<AG extends AlovaGenerics> {
(guardNextConfig?: MiddlewareNextGuardConfig<AG>): Promise<AG['Responded']>;
export interface AlovaGuardNext<AG extends AlovaGenerics, Args extends any[]> {
(guardNextConfig?: MiddlewareNextGuardConfig<AG, Args>): Promise<AG['Responded']>;
}

export type SendHandler<R> = (...args: any[]) => Promise<R>;
export type SendHandler<Args extends any[], R> = (...args: [...Args, ...any[]]) => Promise<R>;
export interface UseHookExportedState<AG extends AlovaGenerics>
extends FrontRequestState<
ExportedState<boolean, AG['StatesExport']>,
Expand All @@ -207,14 +225,17 @@ export interface UseHookExportedState<AG extends AlovaGenerics>
ExportedState<Progress, AG['StatesExport']>,
ExportedState<Progress, AG['StatesExport']>
> {}
export interface UseHookExposure<AG extends AlovaGenerics = AlovaGenerics, SelfType = unknown>
extends UseHookExportedState<AG> {
export interface UseHookExposure<
AG extends AlovaGenerics = AlovaGenerics,
Args extends any[] = any[],
SelfType = unknown
> extends UseHookExportedState<AG> {
abort: () => void;
update: StateUpdater<UseHookExportedState<AG>, AG['StatesExport']>;
send: SendHandler<AG['Responded']>;
onSuccess(handler: SuccessHandler<AG>): IsUnknown<SelfType, this, SelfType>;
onError(handler: ErrorHandler<AG>): IsUnknown<SelfType, this, SelfType>;
onComplete(handler: CompleteHandler<AG>): IsUnknown<SelfType, this, SelfType>;
send: SendHandler<Args, AG['Responded']>;
onSuccess(handler: SuccessHandler<AG, Args>): IsUnknown<SelfType, this, SelfType>;
onError(handler: ErrorHandler<AG, Args>): IsUnknown<SelfType, this, SelfType>;
onComplete(handler: CompleteHandler<AG, Args>): IsUnknown<SelfType, this, SelfType>;
__proxyState: ProxyStateGetter<UseHookExportedState<AG>>;
__referingObj: ReferingObject;
}
Expand All @@ -224,7 +245,7 @@ export const enum EnumHookType {
USE_WATCHER = 2,
USE_FETCHER = 3
}
export interface Hook {
export interface Hook<Args extends any[] = any[]> {
/** 最后一次请求的method实例 */
m?: Method;

Expand All @@ -245,16 +266,16 @@ export interface Hook {

/** event manager */
em: EventManager<{
success: AlovaSuccessEvent<any>;
error: AlovaErrorEvent<any>;
complete: AlovaCompleteEvent<any>;
success: AlovaSuccessEvent<any, Args>;
error: AlovaErrorEvent<any, Args>;
complete: AlovaCompleteEvent<any, Args>;
}>;

/** hookType, useRequest=1, useWatcher=2, useFetcher=3 */
ht: EnumHookType;

/** hook config */
c: UseHookConfig<any>;
c: UseHookConfig<any, Args>;

/** refering object */
ro: ReferingObject;
Expand Down
Loading

0 comments on commit ea20f56

Please sign in to comment.