XXXPage
1、QueryClientProvider
const queryClient = new QueryClient({
...
});
<QueryClientProvider client={queryClient}>
<XXXPage props={props} />
</QueryClientProvider>
QueryClient,管理默认配置和全局状态。
QueryClientProvider 把QueryClient Provider 给 子view children。
export const QueryClientProvider = ({
client,
children,
context,
contextSharing = false,
}: QueryClientProviderProps): JSX.Element => {
...
const Context = getQueryClientContext(context, contextSharing)
return (
<QueryClientSharingContext.Provider value={!context && contextSharing}>
<Context.Provider value={client}>{children}</Context.Provider>
</QueryClientSharingContext.Provider>
)
}
从context里面那到provider的QueryClient, useQueryClient
export const useQueryClient = ({ context }: ContextOptions = {}) => {
const queryClient = React.useContext(
getQueryClientContext(context, React.useContext(QueryClientSharingContext)),
)
...
return queryClient
}
2、useQuery
const { data, isLoading, isError, refetch } = useQuery<XXXResponse>(
['key'],
() => requestXXXInfo()
...
...
return (
...
{isLoading && (
<Loading/>
)}
{isError && (
<NetError/>
)}
{data && ( <View/>
)}
export function useQuery<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
queryKey: TQueryKey,
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
options?: Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn'
>,
): UseQueryResult<TData, TError>
export function useQuery<
TQueryFnData,
TError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
arg1: TQueryKey | UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
arg2?:
| QueryFunction<TQueryFnData, TQueryKey>
| UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
arg3?: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
): UseQueryResult<TData, TError> {
const parsedOptions = parseQueryArgs(arg1, arg2, arg3)
return useBaseQuery(parsedOptions, QueryObserver)
}
useBaseQuery
当每次调用 useQuery 时,内部都会实例一个观察者对象 observer。
observer 会订阅 query 的状态,当这些状态变化时,触发 React 强制更新。
const [observer] = React.useState(
() =>
new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(
queryClient,
defaultedOptions,
),
)
3、QueryCache
export class QueryCache extends Subscribable<QueryCacheListener> {
config: QueryCacheConfig
private queries: Query<any, any, any, any>[]
private queriesMap: QueryHashMap
constructor(config?: QueryCacheConfig) {
super()
this.config = config || {}
this.queries = []
this.queriesMap = {}
}
. . .
. . .
}
4、Retryer 重试器
网络请求,重试、取消、延迟...
export interface Retryer<TData = unknown> {
promise: Promise<TData>
cancel: (cancelOptions?: CancelOptions) => void
continue: () => Promise<unknown>
cancelRetry: () => void
continueRetry: () => void
}
RetryerConfig
interface RetryerConfig<TData = unknown, TError = unknown> {
fn: () => TData | Promise<TData>
abort?: () => void
onError?: (error: TError) => void
onSuccess?: (data: TData) => void
onFail?: (failureCount: number, error: TError) => void
onPause?: () => void
onContinue?: () => void
retry?: RetryValue<TError>
retryDelay?: RetryDelayValue<TError>
networkMode: NetworkMode | undefined
}
export type NetworkMode = 'online' | 'always' | 'offlineFirst'
createRetryer 创建重试器
...
...
export function createRetryer<TData = unknown, TError = unknown>(
config: RetryerConfig<TData, TError>,
): Retryer<TData> {
let isRetryCancelled = false
let failureCount = 0
let isResolved = false
let continueFn: ((value?: unknown) => boolean) | undefined
let promiseResolve: (data: TData) => void
let promiseReject: (error: TError) => void
// Resolve Reject 通知外部
const promise = new Promise<TData>((outerResolve, outerReject) => {
promiseResolve = outerResolve
promiseReject = outerReject
})
const cancel = (cancelOptions?: CancelOptions): void => {
if (!isResolved) {
reject(new CancelledError(cancelOptions))
config.abort?.()
}
}
const cancelRetry = () => {
isRetryCancelled = true
}
const continueRetry = () => {
isRetryCancelled = false
}
const shouldPause = () =>
!focusManager.isFocused() ||
(config.networkMode !== 'always' && !onlineManager.isOnline())
const resolve = (value: any) => {
if (!isResolved) {
isResolved = true
config.onSuccess?.(value)
continueFn?.()
promiseResolve(value)
}
}
const reject = (value: any) => {
if (!isResolved) {
isResolved = true
config.onError?.(value)
continueFn?.()
promiseReject(value)
}
}
const pause = () => {
return new Promise((continueResolve) => {
// continueFn 抛出去 用于continue
continueFn = (value) => {
const canContinue = isResolved || !shouldPause()
if (canContinue) {
// canContinue == true
// Resolve 之后继续执行then
continueResolve(value)
}
return canContinue
}
config.onPause?.()
}).then(() => {
continueFn = undefined
if (!isResolved) {
config.onContinue?.()
}
})
}
// Create loop function
const run = () => {
...
// Value 转promise对象
Promise.resolve(promiseOrValue)
// 执行成功 resolve
.then(resolve)
// 执行失败 开始重试
.catch((error) => {
// Stop if the fetch is already resolved
if (isResolved) {
return
}
// 重试次数
const retry = config.retry ?? 3
// 重试延迟
const retryDelay = config.retryDelay ?? defaultRetryDelay
const delay =
typeof retryDelay === 'function'
? retryDelay(failureCount, error)
: retryDelay
const shouldRetry =
retry === true ||
(typeof retry === 'number' && failureCount < retry) ||
(typeof retry === 'function' && retry(failureCount, error))
if (isRetryCancelled || !shouldRetry) {
// We are done if the query does not need to be retried
reject(error)
return
}
failureCount++
// Notify on fail
config.onFail?.(failureCount, error)
// Delay
sleep(delay)
// Pause if the document is not visible or when the device is offline
.then(() => {
if (shouldPause()) {
return pause()
}
return
})
.then(() => {
if (isRetryCancelled) {
reject(error)
} else {
// loop
run()
}
})
})
}
// Start loop
if (canFetch(config.networkMode)) {
run()
} else {
pause().then(run)
}
return {
promise,
cancel,
continue: () => {
const didContinue = continueFn?.()
// didContinue 继续重试 返回Promise,否者直接resolve
return didContinue ? promise : Promise.resolve()
},
cancelRetry,
continueRetry,
}
}