入门指南
安装
在你的 React 项目目录下,运行以下命令
pnpm add swr
快速上手
对于使用 JSON 数据的普通 RESTful API,首先你需要创建一个 fetcher
函数,它仅仅是原生 fetch
的一个包装器。
const fetcher = (...args) => fetch(...args).then(res => res.json())
如果你想使用 GraphQL API 或 Axios 等库,你可以创建你自己的 fetcher 函数。查看这里获取更多示例。
然后你就可以导入 useSWR
并开始在任何函数组件中使用它。
import useSWR from 'swr'
function Profile () {
const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
// render data
return <div>hello {data.name}!</div>
}
通常情况下,一个请求会有三种可能的状态:"加载中"、"已就绪" 或 "错误"。你可以使用 data
、error
和 isLoading
的值来判断请求的当前状态,并返回相应的 UI。
使其可复用
在构建 Web 应用时,你可能需要在 UI 的多个地方复用数据。在 SWR 之上创建可复用的数据 Hook 非常容易。
function useUser (id) {
const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
isLoading,
isError: error
}
}
然后在你的组件中使用它。
function Avatar ({ id }) {
const { user, isLoading, isError } = useUser(id)
if (isLoading) return <Spinner />
if (isError) return <Error />
return <img src={user.avatar} />
}
通过采用这种模式,你可以忘记以命令式的方式获取数据:启动请求、更新加载状态并返回最终结果。相反,你的代码更加声明式:你只需要指定组件使用什么数据。
示例
在一个真实的例子中,我们的网站显示了一个导航栏和内容,两者都依赖于 user
。
传统上,我们会在顶级组件中使用 useEffect
获取一次数据,并通过 props 将其传递给子组件(注意,我们现在不处理错误状态)。
// page component
function Page () {
const [user, setUser] = useState(null)
// fetch data
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data))
}, [])
// global loading state
if (!user) return <Spinner/>
return <div>
<Navbar user={user} />
<Content user={user} />
</div>
}
// child components
function Navbar ({ user }) {
return <div>
...
<Avatar user={user} />
</div>
}
function Content ({ user }) {
return <h1>Welcome back, {user.name}</h1>
}
function Avatar ({ user }) {
return <img src={user.avatar} alt={user.name} />
}
通常,我们需要将所有数据获取操作都保留在顶级组件中,并将 props 添加到树的每个组件中。如果我们向页面添加更多数据依赖项,代码将变得更难维护。
虽然我们可以使用 Context (在新标签页中打开) 避免传递 props,但仍然存在动态内容问题:页面内容内的组件可以是动态的,顶级组件可能不知道其子组件需要哪些数据。
SWR 完美地解决了这个问题。使用我们刚刚创建的 useUser
Hook,代码可以重构为:
// page component
function Page () {
return <div>
<Navbar />
<Content />
</div>
}
// child components
function Navbar () {
return <div>
...
<Avatar />
</div>
}
function Content () {
const { user, isLoading } = useUser()
if (isLoading) return <Spinner />
return <h1>Welcome back, {user.name}</h1>
}
function Avatar () {
const { user, isLoading } = useUser()
if (isLoading) return <Spinner />
return <img src={user.avatar} alt={user.name} />
}
数据现在绑定到了需要数据的组件,并且所有组件都是相互独立的。所有父组件都不需要知道任何关于数据或传递数据的事情。它们只是渲染。现在代码更简单,更容易维护。
最棒的是,只会有1 个请求发送到 API,因为它们使用相同的 SWR key,并且请求会自动进行去重、缓存和共享。
此外,应用程序现在能够在 用户聚焦或网络重新连接 时重新获取数据!这意味着,当用户的笔记本电脑从睡眠状态唤醒或他们在浏览器标签页之间切换时,数据将自动刷新。