确实 我开始用的时候也很纠结 总感觉哪哪都别扭
现在总结了一套做法感觉还行, 供你参考
首先实现一个useLoadable
方法, 这是一个比较通用的方法,只接收一个异步方法,然后生成状态与结果。
const defaultState = { loading: undefined };
export function useLoadable<E extends Error, T extends AsyncRequest>(
request: T,
initialState: Response<T, E> = defaultState
) {
const [state, setState] = useState<Response<T, E>>(initialState);
const mountedRef = useRef(false);
useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
}, []);
const callRef = useRef(0);
const callbackRef = useRef(request);
useEffect(() => {
callbackRef.current = request;
}, [request]);
const callback = useCallback((...args: Parameters<T>): ReturnType<T> => {
const callId = ++callRef.current;
return callbackRef.current(...args).then(
(value) => {
mountedRef.current &&
callId === callRef.current &&
setState({ value, loading: false });
return value;
},
(error) => {
mountedRef.current &&
callId === callRef.current &&
setState({ error, loading: false });
return error;
}
) as ReturnType<T>;
}, []);
return [state, callback] as [Response<T, E>, typeof callback];
}
类型定义:
type State<T, E> =
| {
loading: undefined;
error?: undefined;
value?: undefined;
}
| {
loading: true;
error?: E | undefined;
value?: T;
}
| {
loading: false;
error: E;
value?: undefined;
}
| {
loading: false;
error?: undefined;
value: T;
};
type AsyncRequest = (...args: any[]) => Promise<any>;
type PromiseType<P extends Promise<any>> = P extends Promise<infer T>
? T
: never;
type Response<T extends AsyncRequest, E> = State<PromiseType<ReturnType<T>>, E>;
使用的例子:
const [state, request] = useLoadable(async (p1: string, p2: string) => {
return await new Promise<{ p1: string; p2: string }>((resolve) =>
setTimeout(() => {
resolve({ p1, p2 });
}, 1000)
);
});
const onClick = () => request('p1', 'p2');
然后对于确定的业务请求进行再次封装,比如:
const useAuth = () => {
const [signinState, signin] = useLoadable(async (username:string, password:string) => {
return await API.signin(username, password);
})
const [signupState, signup] = useLoadable(async (name:string, email:string) => {
return await API.signup(name, email);
});
// state 可以进行自定义合并
return [{...signinState, ...signupState}, signin, signup];
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…