useRequest 使用的插件机制 通过 useRequestImplement 引入插件
// function useRequest<TData, TParams extends any[], TFormated, TTFormated extends TFormated = any>(
// service: Service<TData, TParams>,
// options: OptionsWithFormat<TData, TParams, TFormated, TTFormated>,
// plugins?: Plugin<TData, TParams>[],
// ): Result<TFormated, TParams>
// function useRequest<TData, TParams extends any[]>(
// service: Service<TData, TParams>,
// options?: OptionsWithoutFormat<TData, TParams>,
// plugins?: Plugin<TData, TParams>[],
// ): Result<TData, TParams>
function useRequest<TData, TParams extends any[]>(
service: Service<TData, TParams>, // service就是接口
options?: Options<TData, TParams>, // options就是配置项
plugins?: Plugin<TData, TParams>[], // plugins是插件
) {
return useRequestImplement<TData, TParams>(service, options, [
...(plugins || []),
useDebouncePlugin,
useLoadingDelayPlugin,
usePollingPlugin,
useRefreshOnWindowFocusPlugin,
useThrottlePlugin,
useAutoRunPlugin,
useCachePlugin,
useRetryPlugin,
] as Plugin<TData, TParams>[])
}
function useRequestImplement<TData, TParams extends any[]>(
service: Service<TData, TParams>, // service就是接口,useRequset透传
options: Options<TData, TParams> = {}, // options就是配置项,useRequset透传
plugins: Plugin<TData, TParams>[] = [], // plugins是插件,
) {
const { manual = false, ...rest } = options
// 开发环境
if (isDev) {
if (options.defaultParams && !Array.isArray(options.defaultParams)) {
console.warn(
`expected defaultParams is array, got ${typeof options.defaultParams}`,
)
}
}
// 将配置了默认manual的配置项给fetch
const fetchOptions = {
manual,
...rest,
}
// 始终保存最新的请求函数
const serviceRef = useLatest(service)
// 强制组件重新渲染的函数
const update = useUpdate()
// Fetch实例处理请求,useCreation确保实例不会重复创建
const fetchInstance = useCreation(() => {
// 调用插件的初始化钩子,然后获取Fetch初始值,如useAutoRunPlugin配置了loading初始值,注意和其他钩子在插件中写法不一样,这个是直接挂在函数上的,而不是返回值上
const initState = plugins
.map((p) => p?.onInit?.(fetchOptions))
.filter(Boolean)
return new Fetch<TData, TParams>(
serviceRef,
fetchOptions,
update,
Object.assign({}, ...initState),
)
}, [])
// 在fetchInstance保存fetchOptions,为了fetchInstance调用options里的钩子,但是为什么类中没定义这个变量?
fetchInstance.options = fetchOptions
// 执行所有插件返回的包含多个钩子的对象
fetchInstance.pluginImpls = plugins.map((p) => p(fetchInstance, fetchOptions))
// 组件渲染时,未配置手动请求时,发起请求
useMount(() => {
if (!manual) {
// useCachePlugin can set fetchInstance.state.params from cache when init
const params = fetchInstance.state.params || options.defaultParams || []
// @ts-ignore
fetchInstance.run(...params)
}
})
// 组件销毁时取消请求
useUnmount(() => {
fetchInstance.cancel()
})
return {
loading: fetchInstance.state.loading,
data: fetchInstance.state.data,
error: fetchInstance.state.error,
params: fetchInstance.state.params || [],
cancel: useMemoizedFn(fetchInstance.cancel.bind(fetchInstance)),
refresh: useMemoizedFn(fetchInstance.refresh.bind(fetchInstance)),
refreshAsync: useMemoizedFn(fetchInstance.refreshAsync.bind(fetchInstance)),
run: useMemoizedFn(fetchInstance.run.bind(fetchInstance)),
runAsync: useMemoizedFn(fetchInstance.runAsync.bind(fetchInstance)),
mutate: useMemoizedFn(fetchInstance.mutate.bind(fetchInstance)),
} as Result<TData, TParams>
}
核心代码
export default class Fetch<TData, TParams extends any[]> {
// 插件钩子对象的数组
pluginImpls: PluginReturn<TData, TParams>[]
// 记录的请求次数
count: number = 0
// 状态
state: FetchState<TData, TParams> = {
loading: false,
params: undefined,
data: undefined,
error: undefined,
}
constructor(
public serviceRef: MutableRefObject<Service<TData, TParams>>, // service
public options: Options<TData, TParams>, // options
public subscribe: Subscribe, // 强制渲染的函数
public initState: Partial<FetchState<TData, TParams>> = {}, // 初始状态
) {
// 设置初始状态
this.state = {
...this.state,
loading: !options.manual,
...initState,
}
}
// 设置状态并强制渲染,没有用react的useState来保证状态改变时渲染,而是用强制渲染新数据实现
setState(s: Partial<FetchState<TData, TParams>> = {}) {
this.state = {
...this.state,
...s,
}
this.subscribe()
}
// 执行钩子
runPluginHandler(event: keyof PluginReturn<TData, TParams>, ...rest: any[]) {
// 遍历插件钩子对象的数组,执行对应钩子
const r = this.pluginImpls.map((i) => i[event]?.(...rest)).filter(Boolean)
// 返回钩子给的状态
return Object.assign({}, ...r)
}
// 核心请求函数
async runAsync(...params: TParams): Promise<TData> {
// 请求次数加一
this.count += 1
const currentCount = this.count
// 执行插件onBefore钩子,并获取钩子给的状态
const {
stopNow = false,
returnNow = false,
...state
} = this.runPluginHandler('onBefore', params)
// 如果钩子给了stopNow就停止
// stop request
if (stopNow) {
return new Promise(() => {})
}
// 强制渲染新状态
this.setState({
loading: true,
params,
...state,
})
// 如果钩子给了stopNow就返回
// return now
if (returnNow) {
return Promise.resolve(state.data)
}
// 调用配置里的钩子onBefore
this.options.onBefore?.(params)
try {
// 获取真正的请求函数
let { servicePromise } = this.runPluginHandler(
'onRequest',
this.serviceRef.current,
params,
)
if (!servicePromise) {
servicePromise = this.serviceRef.current(...params)
}
// 开始请求,res是promise的then
const res = await servicePromise
// 取消请求
if (currentCount !== this.count) {
// prevent run.then when request is canceled
return new Promise(() => {})
}
// const formattedResult = this.options.formatResultRef.current ? this.options.formatResultRef.current(res) : res;
// 强制渲染新状态
this.setState({
data: res,
error: undefined,
loading: false,
})
// 调用配置里的钩子onSuccess和插件里的
this.options.onSuccess?.(res, params)
this.runPluginHandler('onSuccess', res, params)
// 调用配置里的钩子onFinally
this.options.onFinally?.(params, res, undefined)
// 调用插件里的钩子onFinally
if (currentCount === this.count) {
this.runPluginHandler('onFinally', params, res, undefined)
}
// 返回promise的then
return res
} catch (error) {
if (currentCount !== this.count) {
// prevent run.then when request is canceled
return new Promise(() => {})
}
this.setState({
error,
loading: false,
})
this.options.onError?.(error, params)
this.runPluginHandler('onError', error, params)
this.options.onFinally?.(params, undefined, error)
if (currentCount === this.count) {
this.runPluginHandler('onFinally', params, undefined, error)
}
throw error
}
}
// 处理了promise的then的错误的函数
run(...params: TParams) {
this.runAsync(...params).catch((error) => {
if (!this.options.onError) {
console.error(error)
}
})
}
// 取消请求
cancel() {
this.count += 1
this.setState({
loading: false,
})
this.runPluginHandler('onCancel')
}
// 再请求一次
refresh() {
// @ts-ignore
this.run(...(this.state.params || []))
}
// 返回新请求的promise的then
refreshAsync() {
// @ts-ignore
return this.runAsync(...(this.state.params || []))
}
// 修改data
mutate(data?: TData | ((oldData?: TData) => TData | undefined)) {
const targetData = isFunction(data) ? data(this.state.data) : data
// 调用插件里的钩子onMutate
this.runPluginHandler('onMutate', targetData)
// 强制渲染新状态
this.setState({
data: targetData,
})
}
}