在 React Hook 兴起之后,关于数据获取的库 swr 也跟着突然流行起来。
函数式组件的出现,但却一直困扰于数据状态的处理,直到 React Hook 的出现,使得状态更加的隔离。尽管使用 useState 来改变组件内部状态,不过在数据获取上,还是缺少了点数据驱动的意味。 swr 就是针对与此,使得我们可以在 Hook 中更优雅的获取数据,其核心思想就是通过 use 组件来返回 data, isLoading, error 等相关数据,这样组件可以更加的处理 UI 相关的展示。
本文不是对 swr 源码的解读,只是在看代码之中的一些感悟。目前相关版本 0.1.13。
数据获取
首先关于数据获取常见的一些手段,如接口数据缓存,错误重试,数据格式约定校验等等。
swr 源于 stale-while-revalidate,HTTP RFC 5861 的缓存失效策略。其核心思想是当缓存过期的时候,可以先直接使用缓存,等数据更新后再重新刷新数据。其实在 Cache-Control 中也有该属性。
存储方便很常规,new Map() 的 Hash Key 来存储各数据。
全局配置
swr 函数还贴心的提供全局的配置,核心是使用 Context Provider 方法来设置,最终和 useSWR merge 得到。
数据请求
在数据请求相关方面,swr 还是有蛮多的小细节设计。比如本地数据缓存(优化组件销毁重新创建时的渲染),超时重试,条件取数据等等,甚至还支持了 GraphQL 的方式。
依赖请求
使用 try..catch + onErrorRetry 来实现多个请求的顺序执行。在 service 未获取到数据的时候,进入 catch 并执行 onErrorRetry 来不断的重新执行。方案稍微有点暴力,不过正常场景下适用了。
在 onErrorRetry 中,有累计重试的机制。
const timeout =
~~((Math.random() + 0.5) * (1 << count)) * config.errorRetryInterval
setTimeout(revalidate, timeout, opts)
requestIdleCallback
设计的时候还贴心的考虑到了如果浏览器繁忙的时候的处理。
// 伪代码
if (window['requestIdleCallback']) {
window['requestIdleCallback'](softRevalidate)
} else {
softRevalidate()
}
SSR 的支持
SSR 的方案,先通过 initialData 传入服务的获取到的数据,然后再前端再次请求,通过 useEffect 和 useLayoutEffect 区分渲染客户端还是服务端。
细节一
data, error, isValidating 如果分开 setState 的话,会多次触发,swr 中使用 useReducer 来合并数据已达到更新一次的目的。
细节二
使用 useRef 来存储 update 状态值,因为 useRef 返回的是一个可变对象,详见官方说明,一般常用场景如存一个布尔值来表示是否首次渲染,或者手动存储上一轮的 state 属性等等。