// TODO: introduce namespace for the cache serializeKey(key: keyInterface): [string, any, string] { let args = null if (typeof key === 'function') { try { key = key() } catch (err) { // dependencies not ready key = '' } }
if (Array.isArray(key)) { // args array args = key // 作者在hash中使用了weakmap // use WeakMap to store the object->key mapping // so the objects can be garbage collected. // WeakMap uses a hashtable under the hood, so the lookup // complexity is almost O(1). key = hash(key) } else { // convert null to '' key = String(key || '') }
// React currently throws a warning when using useLayoutEffect on the server. // To get around it, we can conditionally useEffect on the server (no-op) and // useLayoutEffect in the browser. const useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect
let shouldDeduping = typeof CONCURRENT_PROMISES[key] !== 'undefined' && revalidateOpts.dedupe
if (shouldDeduping) { // there's already an ongoing request, // this one needs to be deduplicated. startAt = CONCURRENT_PROMISES_TS[key] newData = await CONCURRENT_PROMISES[key] } else { // if no cache being rendered currently (it shows a blank page), // we trigger the loading slow event. if (config.loadingTimeout && !cache.get(key)) { setTimeout(() => { if (loading) eventsRef.current.emit('onLoadingSlow', key, config) }, config.loadingTimeout) }
// Focus revalidate let eventsBinded = false if (typeofwindow !== 'undefined' && window.addEventListener && !eventsBinded) { const revalidate = () => { if (!isDocumentVisible() || !isOnline()) return
for (let key in FOCUS_REVALIDATORS) { if (FOCUS_REVALIDATORS[key][0]) FOCUS_REVALIDATORS[key][0]() } } window.addEventListener('visibilitychange', revalidate, false) window.addEventListener('focus', revalidate, false) // only bind the events once eventsBinded = true }
然后对于在过程里面的代码则是这样的:
// whenever the window gets focused, revalidate let onFocus if (config.revalidateOnFocus) { // throttle: avoid being called twice from both listeners // and tabs being switched quickly onFocus = throttle(softRevalidate, config.focusThrottleInterval) if (!FOCUS_REVALIDATORS[key]) { FOCUS_REVALIDATORS[key] = [onFocus] } else { FOCUS_REVALIDATORS[key].push(onFocus) } }
// always update error // because it can be `undefined` if (stateRef.current.error !== updatedError) { newState.error = updatedError needUpdate = true }
if (needUpdate) { dispatch(newState) }
if (shouldRevalidate) { if (dedupe) { return softRevalidate() } else { return revalidate() } } returnfalse }
// add updater to listeners if (!CACHE_REVALIDATORS[key]) { CACHE_REVALIDATORS[key] = [onUpdate] } else { CACHE_REVALIDATORS[key].push(onUpdate) }
// define returned state // can be memorized since the state is a ref useMemo(() => { const state = { revalidate, mutate: boundMutate } as responseInterface< Data, Error > Object.defineProperties(state, { error: { // `key` might be changed in the upcoming hook re-render, // but the previous state will stay // so we need to match the latest key and data (fallback to `initialData`) get: function () { stateDependencies.current.error = true return keyRef.current === key ? stateRef.current.error : initialError }, enumerable: true }, data: { get: function () { stateDependencies.current.data = true return keyRef.current === key ? stateRef.current.data : initialData }, enumerable: true }, isValidating: { get: function () { stateDependencies.current.isValidating = true return stateRef.current.isValidating }, enumerable: true } })