正好昨天处理了一个类似的问题,按理说这种公共组件最好别有请求,但团队里其他人已经这么写了,而且代码已经测试过了,所以不好大改。
我这边是用一个单例去保存每次请求的结果,最后一段代码里的1000代表缓存的超时时间为1000ms(1s),如果在1s内一个请求再次发起,如果这个请求和上次请求的参数一致,就使用上次的缓存,当然如果上次请求还在pending,也不会重新发起,会一并等上次的结果。具体超时时间设多少看接口的特性,数据不会频繁变的话,设十几二十秒也可以。
最后用这个hook返回的apiCall去替换之前的请求方法就行。
import { useCallback, useState } from "react";
import { isEqual } from "lodash";
interface ResponseInfo {
params: unknown[];
lastRequestTime: number;
getData: () => Promise;
}
type PromiseValue = T extends Promise ? U : T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type CommonFunc = (...p: any[]) => any;
type ResponseData = {
data: T;
isSuccess: boolean;
isError: boolean;
};
type ResponseValue = {
apiCall: F;
data: ResponseData>>;
loading: boolean;
};
const responseWeakMap = new WeakMap();
const genUseSingletonApi =
(apiCall: T) =>
(instance: CommonFunc, expiration: number) =>
() => {
type APIReturnData = PromiseValue>;
const [data, setData] = useState();
const [loading, setLoading] = useState();
const apiCallFunc = useCallback(
async (...params: Parameters) => {
setLoading(true);
const endCallback = (res: APIReturnData) => {
setLoading(false);
setData(res);
};
const requestInfo = responseWeakMap.get(instance);
if (requestInfo) {
const { getData: getResponseInfo, params: oldParams, lastRequestTime } = requestInfo;
const hasResponse =
isEqual(oldParams, params) && +new Date() - lastRequestTime void = () => {};
const promise: Promise = new Promise(resolveCb => {
resolve = resolveCb;
});
responseWeakMap.set(instance, {
getData: () => promise,
params,
lastRequestTime: +new Date(),
});
const result = await apiCall(...params);
resolve(result);
endCallback(result);
return result;
},
[],
);
return {
apiCall: apiCallFunc,
data,
loading,
};
};
const someAPISingletonInstance = genUseSingletonApi(fetchDataFunc);
export const useAccountingList = someAPISingletonInstance(
someAPISingletonInstance,
1000,
);