Skip to content
开发文档
入门

入门

¥Getting Started

安装

¥Installation

在 React 项目目录中,运行以下命令:

¥Inside your React project directory, run the following:

pnpm add swr

快速开始

¥Quick Start

对于使用 JSON 数据的普通 RESTful API,首先需要创建一个 fetcher 函数,它只是原生 fetch 的封装:

¥For normal RESTful APIs with JSON data, first you need to create a fetcher function, which is just a wrapper of the native fetch:

const fetcher = (...args) => fetch(...args).then(res => res.json())
💡

如果你想使用 GraphQL API 或像 Axios 这样的库,你可以创建自己的 fetcher 函数。查看 here 了解更多示例。

¥If you want to use GraphQL API or libs like Axios, you can create your own fetcher function. Check here for more examples.

然后你可以导入 useSWR 并开始在任何函数组件中使用它:

¥Then you can import useSWR and start using it inside any function components:

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>
}

通常,请求有 3 种可能的状态:"加载中"、"就绪" 或 "错误"。你可以使用 dataerrorisLoading 的值来判断当前请求的状态,并返回相应的 UI。

¥Normally, there're 3 possible states of a request: "loading", "ready", or "error". You can use the value of data, error and isLoading to determine the current state of the request, and return the corresponding UI.

使其可重复使用

¥Make It Reusable

构建 Web 应用时,你可能需要在 UI 的许多位置重用数据。在 SWR 之上创建可重用的数据钩子非常容易:

¥When building a web app, you might need to reuse the data in many places of the UI. It is incredibly easy to create reusable data hooks on top of SWR:

function useUser (id) {
  const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
 
  return {
    user: data,
    isLoading,
    isError: error
  }
}

并在你的组件中使用它:

¥And use it in your components:

function Avatar ({ id }) {
  const { user, isLoading, isError } = useUser(id)
 
  if (isLoading) return <Spinner />
  if (isError) return <Error />
  return <img src={user.avatar} />
}

通过采用这种模式,你可以忘记以命令式方式获取数据:启动请求,更新加载状态,并返回最终结果。相反,你的代码更具声明性:你只需要指定组件使用哪些数据。

¥By adopting this pattern, you can forget about fetching data in the imperative way: start the request, update the loading state, and return the final result. Instead, your code is more declarative: you just need to specify what data is used by the component.

示例

¥Example

在现实世界的示例中,我们的网站显示导航栏和内容,两者都取决于 user

¥In a real-world example, our website shows a navbar and the content, both depend on user:

传统上,我们在顶层组件中使用 useEffect 获取一次数据,然后通过属性将其传递给子组件(请注意,我们现在不处理错误状态):

¥Traditionally, we fetch data once using useEffect in the top level component, and pass it to child components via props (notice that we don't handle error state for now):

// 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} />
}

通常,我们需要将所有数据请求保留在顶层组件中,并向树深处的每个组件添加属性。如果我们向页面添加更多数据依赖,代码将变得更难以维护。

¥Usually, we need to keep all the data fetching in the top level component and add props to every component deep down the tree. The code will become harder to maintain if we add more data dependency to the page.

虽然我们可以避免使用 上下文 (opens in a new tab) 传递属性,但仍然存在动态内容问题:页面内容内的组件可以是动态的,顶层组件可能不知道其子组件需要什么数据。

¥Although we can avoid passing props using Context (opens in a new tab), there's still the dynamic content problem: components inside the page content can be dynamic, and the top level component might not know what data will be needed by its child components.

SWR 完美解决了这个问题。使用我们刚刚创建的 useUser 钩子,代码可以重构为:

¥SWR solves the problem perfectly. With the useUser hook we just created, the code can be refactored to:

// 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} />
}

数据现在绑定到需要数据的组件,并且所有组件都是相互独立的。所有父组件不需要了解有关数据或传递数据的任何信息。它们只是渲染。现在代码更加简单并且更容易维护。

¥Data is now bound to the components which need the data, and all components are independent to each other. All the parent components don't need to know anything about the data or passing data around. They just render. The code is much simpler and easier to maintain now.

最美妙的是,只有 1 个请求发送到 API,因为它们使用相同的 SWR 键,并且请求会自动进行数据去重、缓存和共享。

¥The most beautiful thing is that there will be only 1 request sent to the API, because they use the same SWR key and the request is deduped, cached and shared automatically.

此外,应用现在能够重新获取 用户焦点或网络重新连接 上的数据!这意味着,当用户的电脑从睡眠状态唤醒或在浏览器选项卡之间切换时,数据将自动刷新。

¥Also, the application now has the ability to refetch the data on user focus or network reconnect! That means, when the user's laptop wakes from sleep or they switch between browser tabs, the data will be refreshed automatically.