安安!前几天讲了怎麽在专案里用些 data fetching functions 做 pre-rendering。不过如果想要直接在 client-side 抓取资料呢?有什麽方法?~ 今天跟大家分享一个 data fetching (抓取资料) 的 library,SWR!
SWR 是 stale-while-revalidate
的简称,意思是我们抓取资料的时候,会先拿到 cache 里 (stale / 旧资料) 的,然後在背後去发请求抓取资料 (revalidate),再回传最新的资料。SWR 是一个提供抓取资料功能的 react hooks library,没错,他是 react hooks!
在 client-side 抓取资料算简单,可是也必须注意一些事情,例如重复呼叫某 API。有了 SWR 之後,我们不用再担心这件事!SWR 帮我们做这些:
很简单~ 在专案的目录下,输入以下指令:
yarn add swr
# or
npm install swr
装完就可以这样使用:
import useSWR from 'swr'
function User() {
// Github API: https://api.github.com/users/vercel
const { data, error } = useSWR('https://api.github.com/users/vercel', fetcher)
if (error) return <div>发生错误 :o</div>
if (!data) return <div>loading...</div>
return <div>Hi {data.name}!</div> // Hi Vercel!
}
这里 useSWR
hooks 接收两个 parameters,key
和 fetcher
function。
key
:SWR 会根据这 key 做 cache,通常是请求的 API URL
fetcher
:去做抓取的 function,这 function 接收 key
当 parameter 然後回传 data (资料)。可以用一般的 fetch
,axios
,或是 GraphQL
,只要可以用 key
去抓取和回传资料都可以
const fetcher = (...args) => fetch(...args).then(res => res.json())
上面那段 code 只能抓取 vercel 这 Github user 的资料,如果我们想抓其他人的资料呢?要复制贴上吗?不用~ 我们可以做 React custom hook:
function useUser (id) {
const { data, error } = useSWR(`https://api.github.com/users/${id}`, fetcher)
return {
user: data,
isLoading: !error && !data,
isError: error
}
}
然後在 React component 里使用:
// Avatar component
function Avatar ({ id }) {
const { user, isLoading, isError } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <img src={user.avatar_url} alt={user.name} />
}
那如果有不只一个 component 需要用到 user 的资料呢?useUser
hook 可以在很多 component 里重复使用喔:
// Info component
function Info ({ id }) {
const { user, isLoading } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <h1>Welcome back, {user.name} (@{user.login})</h1>
}
是不是很方便!而且 SWR 会自动做 cache 还有避免重复发请求,虽然我们使用了 useUser
两次,可是实际上我们只发一次 API request 喔!这麽做可以减少 props 传递的动作,每个独立的 component 自己处理资料~
服务器端或是资料库里的资料有可能会变,我们该怎麽拿到最新值?该怎麽跟服务器做同步?SWR 的 revalidation 机制成为答案!有几个 revalidate 时机:
revalidateOnFocus
):这里的 focus 是指 浏览器 tab 的 focus,使用者有没有正在看该 tab,如果没有,每次使用者回到这 tab,SWR 会做 revalidate (重新抓取资料),让使用者看到最新的资料refreshInterval
):定时去做 revalidate,只要画面上有用到这些资料,SWR 会根据我们设定的 interval 去做资料更新,让画面看起来有即时更新revalidateOnReconnect
):使用者有可能会断线,例如电脑进入睡眠模式,当使用者重新连到网路时,SWR 会做 revalidate 更新资料~useSWRImmutable
):以上三种 revalidate 时机会让资料保持同次的状态,可是如果资料完全不会变,那何必 revalidate 呢?我们可以把 revalidate 机制关掉,让 SWR 永远不做资料同步,只抓一次资料!看起来都很棒,想要开始用在 Next.js 的专案里,该怎麽用呢?
其实如果想要在浏览器端去抓取资料的话,直接用 useSWR
在 component 里就可以~ 就像上面的 code 一样,基本上就是在 React components 里面使用 useSWR
或是 custom SWR hooks 喔!
可是 Next.js 又可以做 pre-rendering,例如 SSG 或是 SSR,那还是可以用 SWR 吗?当然可以的!SWR 是处理 client-side 的资料抓取方式,我们只要可以在 build time 时拿到资料让页面不完全是空的,然後 request time 时在浏览器再重新抓取最新资料。之前如果用 getStaticProps
或是 getServerSideProps
时,我们都会把 props
传到 page (页面) component 里。可是用 SWR 的方式比较不一样:
// 一样在 getStaticProps 抓取资料
export async function getStaticProps () {
const article = await getArticleFromAPI()
// 要回传 fallback 的资料!
return {
props: {
fallback: {
// 这 key 要跟 useSWR 用的 key 一样喔~
'/api/article': article
}
}
}
}
// Article component
function Article() {
// 因为 fallback 有包含 `/api/article` 这 key,
// data 预设值会是 fallback 里设定的值
// 当使用者在浏览器看到这画面时,SWR 会抓取最新料
const { data } = useSWR('/api/article', fetcher)
return <h1>{data.title}</h1>
}
// Page component 收到 getStaticProps 回传的 props.fallback
export default function Page({ fallback }) {
// SWRConfig 去做设定,在这里设定 fallback 值
return (
<SWRConfig value={{ fallback }}>
<Article />
</SWRConfig>
)
}
所以产生页面时会先塞资料进 HTML 档案,不过当使用者在浏览器看到这画面时,SWR 会抓取最新料,更新 HTML 里原本的内容。
如果大家对於 client-side data fetching 有兴趣或需求,真的可以去看看 SWR!简单又方便,还能做 cache,也不用做 props drilling,实在很开心~ 不过我目前也没有在大专案用过 SWR,所以不太清楚会不会不适用在较复杂的专案Q
祝大家周末愉快!
晚安 <3
>>: [30天 Vue学好学满 DAY24] Vue Router-3
系列文接近尾声,专案最後要来做一个过渡动画效果 我们要做的效果是向左/右滑入滑出,效果可以参考 V...
Redux Redux 跟 React 并没有关系。你可以用 React、Angular、Ember...
看大家都写leetcode o3o Input 传入一个已排序好的阵列位置,把它变成se...
今天的目标: 要怎麽样依照范本复制并改动 Google Sheet,并一次性地的将结果搜集到同一份 ...
在反覆执行 rspec 时会一直需要载入环境,这时候可以透过 spring 帮助你更快的执行 rsp...