专栏目录: 全网最细 React-Query源码探秘 - 不月阳九的专栏 - 掘金 (juejin.cn)
Query —— React-Query的底层核心类
主要功能 :网络数据请求、状态变化的处理、内存回收。
Query的工作机制
- 每个Query通过queryKey进行区别, Query上保存了请求回来的后端数据,一个请求方法API对应一个Query。
- 同一个数据(Query)可能会被多个react组件订阅, 每个react组件作为一个Observer,存放在Query的 observers属性中.
- 当Query中的数据发生更新 会遍历observers,通知所有订阅的React组件,更新页面.
- Observer挂载时会传入组件的setState函数作为updater,以此执行更新.
网络数据请求
Query发起请求时,会创建一个retryer,用来真正发起请求,详见第五篇Retryer
状态变化处理
这里使用的是Redux的 dispatch action reducer一套, 当请求结束(比如成功) 会创建一个success的action, dispatch分发到Query上, reducer执行对应的action,改变Query的状态
内存回收
query作为缓存,当然有有效时间,当我们配置的cacheTime后,在Query初始化时会创建一个setTimeout,定时后从缓存中清除Query,并且通知所有的observer,同时通知QueryCache,删除这个Query。
Query的状态
分为请求状态和查询状态,请求结束后会针对不同的情况更改状态 ,并return为结果,我们可以在外层查询到状态
export type QueryStatus = 'loading' | 'error' | 'success' // 当前单次请求状态
export type FetchStatus = 'fetching' | 'paused' | 'idle'// 查询状态 表示是否在持续查询
源码解析(仅取重要部分)
(以下非源码 而是自己手写的迷你版React-Query 方便大家理解)
export class Query {
...
this.cache = config.cache
queryKey: QueryKey // 唯一key
options: QueryOptions // 配置项
state: QueryState // query的状态
observers: QueryObserver[] // 订阅者
private retryer?: Retryer // 请求器
private GCtimer?: ReturnType<typeof setTimeout>// 垃圾回收timer
constructor(config: QueryConfig) {
...
this.state = config.state || getDefaultQueryState()
console.log('创建Query', config.options.queryKey);
this.updateGCTimer() // 垃圾回收定时器
}
addObserver(){...} // 添加一个ob
removeObserver(){...} // 移除一个ob
destory(){...} // query自我销毁
updateGCTimer() { // 垃圾回收定时器
const cacheTime = this.options.cacheTime
if (!cacheTime) return
this.GCtimer = setTimeout(() => {
this.destory()
clearTimeout(this.GCtimer)
}, cacheTime)
}
fetch(){// 请求数据
...
this.retryer = createRetryer({// 通过创建retryer进行请求
fn: fetchFn,
// abort: false, // 终止指针
onSuccess,
onError,
onFail,
onPause,
onContinue,
retry: options.retry, // 重复次数
retryDelay: options.retryDelay,// 延迟时间
})
// 将retryer返回的结果保存 并返回
this.promise = this.retryer.promise
return this.promise
}
// 根据action创建创建不同的reducer 来修改当前query的状态
private dispatch(action: Action): void {
const reducer = (state: QueryState): QueryState => {
switch (action.type) {
case 'fetch':
console.log('发起请求事件');
return {
...state,
fetchFailureCount: 0,
fetchStatus: 'fetching',
status: 'loading'
}
case 'success':
console.log('请求成功事件');
return {
...state,
data: action.data,
dataUpdateCount: state.dataUpdateCount + 1,
dataUpdatedAt: action.dataUpdatedAt ?? Date.now(),
error: null,
status: 'success',
fetchStatus: 'idle'
}
case 'failed':
console.log('请求失败事件');
return {
...state,
fetchFailureCount: state.fetchFailureCount + 1,
}
case 'error':
const error = action.error
console.log('请求错误事件');
return {
...state,
error: error,
errorUpdateCount: state.errorUpdateCount + 1,
fetchFailureCount: state.fetchFailureCount + 1,
status: 'error',
fetchStatus: 'idle'
}
case 'pause':
console.log('请求暂停事件');
return {
...state,
fetchStatus: 'paused',
}
case 'continue':
console.log('请求继续事件');
return {
...state,
fetchStatus: 'fetching',
}
}
}
this.state = reducer(this.state) //修改query的state
// 通知所有的observer state更新了 observer重新渲染组件
this.observers.forEach((observer) => {
observer.onQueryUpdate(action)
})
}
}