一开始想着不就是把请求变成 hook 写法来用吗?就是多了一个方便 loading 标志位的处理。抱着试一试的心态用了,用着用着感觉还挺不错,记录下使用方法。
一、基础用法
在一些进入页面就要请求的场景里,比如列表页,配置项什么的,特别合适。传统的写法可能是 useEffect(() => {}, [])
里触发一下接口请求。用了 swr 后就可以不用这么写了,官网示例:
import useSWR from 'swr'
function Profile() {
const { data, error, isLoading, mutate } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
在传统场景可能就需要声明一个 loading 变量,然后自行管理,先 setLoading(true) 又 setLoading(false)。同理,data 也得用 useState 处理一下。这里直接把数据、错误,等待标志位,摊平来使用。页面一多,还是便利不少的,特别是 B 端的列表页面。在编辑完信息后,需要更新内容可以主动调用一次 mutate
来触发一次请求。
二、使用场景
众所周知,hook 不能写在条件里,所以这个很适合写在一开始必须请求的场景。当然 swr 也是预留了这个场景,如果开始传入的是空路径,就不会请求。也是官网示例:条件数据请求
function Profile () {
const { data } = useSWR(isReady ? '/api/user' : null, fetcher, { suspense: true })
// 如果 `isReady` 是 false,`data` 将会是 `undefined`
// ...
}
这个可以在需要有前置条件的时候,比如第二个接口依赖于第一个接口返回的某个字段,这时候就可以先这样按住第二个接口。
这个也可以用于用户行为后的请求,比如按钮点击后的,登录什么的。但我觉得这种场景下,用 swr 就很别扭。而且一般都会把接口声明在一个地方,方便维护。那么此时这两种场景下的接口写法就不一样,会出现一部分接口一定是夹杂了条件判断。
所以,我认为接口还是按照原来的声明,只是在列表之类的接口,要使用的时候再看情况用 swr 包装一层。如:
const getUser = () => fetcher('/api/user')
function Profile() {
const { data, error, isLoading } = useSWR('/api/user', () => getUser())
}
三、携带参数
接口免不了要携带参数,比如列表搜索也有一堆过滤参数,可以这样写:
import useSWR from 'swr'
function Profile() {
const [formData] = useState({ a: 1 })
const { data, error, isLoading } = useSWR(['/api/user', formData], ([, params]) => fetcher(params))
}
// 不要写成下面这种
// function Profile() {
// const [formData] = useState({ a: 1 })
// const { data, error, isLoading } = useSWR('/api/user', () => fetcher(formData))
// }
这样可以达到修改参数 formData 的时候,就会自动触发带上新的参数一次请求。**注意不要写成上面第二种,这样参数相当于写在闭包里,再次请求会变成携带旧的参数。
四、工具参数
有一些很实用的参数:选项,这里列举几个改到默认配置的:
keepPreviousData
改为 true,默认为 false,在列表重新请求时会把数据先置空,这时列表就白了。改为 true 后就是数据来了再更新,不会有这个闪的效果。revalidateOnFocus
改为 false,默认为 true,在切换 tab 的时候,回到本页面也就是页面 focus 事件触发一次请求,可能大部分场景不需要这样。focusThrottleInterval
如果需要聚焦时再度请求,可以搭配这个限流一下。refreshInterval
这个就顾名思义了,开启轮询。
可以单个使用:
const { data } = useSWR('/api/user', fetcher, { keepPreviousData: true })
每个都写太麻烦,可以全局配置,然后个别灵活处理:
<SWRConfig value={{ keepPreviousData: true }}>
<Page />
</SWRConfig>