发布于 2026-01-06 3 阅读
0

Next.js 中的数据获取——如何使用 SWR

Next.js 中的数据获取——如何使用 SWR

Next.js 提供了多种数据获取方式,因为它同时支持客户端渲染和服务端渲染。其中一种方式是使用 SWR,它是一组用于远程数据获取的 React Hooks。

在本教程中,我们将学习 SWR,这是一个可以简化缓存、分页、重新验证等操作的库。我们还将构建一个 Next 应用(客户端),该应用使用 SWR 从JSON Placeholder中检索数据。

我们开始吧!

什么是驻波比(SWR)?

SWR 是 React 的缩写。它是一个轻量级库,由Next.js 的stale-while-revalidate同一团队创建。它允许使用 React Hooks 实时获取、缓存或重新获取数据。SWR 的工作流程分为三个步骤:首先,它返回缓存(过时数据);然后,它从服务器获取数据(重新验证);最后,它返回最新数据。这样,SWR 可以在从服务器获取新数据的同时向用户显示其他内容,从而提升用户体验。

SWR 与后端无关,这意味着你可以使用它从任何支持 HTTP 请求的服务器检索数据。它还对 TypeScript 和服务器端渲染提供了良好的支持。

也就是说,我们可以动手实践,搭建一个新的 Next.js 应用来使用 SWR 功能。

设置

要创建新应用,我们将选择“创建下一个应用”。
首先打开命令行界面 (CLI) 并运行以下命令:

    npx create-next-app next-swr-app
Enter fullscreen mode Exit fullscreen mode

安装 SWR 软件包:

    npm install swr
Enter fullscreen mode Exit fullscreen mode

接下来,按如下方式构建文件夹结构:

├── components
| └── Post.js
├── pages
| └── index.js
├── useRequest.js
└── package.json
Enter fullscreen mode Exit fullscreen mode

让我们来分析一下文件结构:

  • Post.js是负责显示帖子对象的组件。
  • index.js这是我们应用程序的主页。
  • useRequest.js是一个自定义钩子,可以帮助使用 SWR 获取数据。

有了这样的文件夹结构,我们就可以在下一节中开始从 JSON Placeholder 中检索远程数据了。

使用以下方式获取数据useSWR

要使用 SWR 获取远程数据,我们可以使用 `fetch`useSWR或 `pash`useSWRInfinite钩子。但是,这两个钩子之间存在一些区别。前者仅用于数据获取,而后者可以检索和分页数据。您可以使用 `fetch` 钩子useSWRInfinite快速地在 Next.js 应用中添加无限滚动或分页功能。

现在,让我们来浏览一下这个文件useRequest.js

import useSWR from "swr"

const fetcher = url => fetch(url).then(res => res.json())
const baseUrl = "https://jsonplaceholder.typicode.com"

export const useGetPosts = path => {
  if (!path) {
    throw new Error("Path is required")
  }

  const url = baseUrl + path

  const { data: posts, error } = useSWR(url, fetcher)

  return { posts, error }
}
Enter fullscreen mode Exit fullscreen mode

使用此自定义钩子获取数据是可选的。您也可以直接在组件中使用 SWR 钩子。

fetcher函数允许我们向服务器发送 HTTP 请求,然后将响应数据解析为 JSON 格式。该fetch方法来自unfetchNext.js 自带的包。

接下来,我们使用该useGetPosts函数通过钩子发送查询useSWR。它需要接收url服务器地址和fetcher用于执行查询的函数作为参数。数据检索完成后,我们返回获取到的数据postserror状态。

有了这个自定义钩子,我们现在可以创建组件来显示获取到的帖子了。

创建组件

  • components/Post.js
export default function Post({ post }) {
  const { title, body, id } = post
  return (
    <div className="Card">
      <h1 className="Card--title">
        {id}. {title}
      </h1>
      <p className="Card--body">{body}</p>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

如您所见,我们有一个简单的组件,它接收一个post要显示的内容作为参数。然后,我们使用解构赋值的方式从对象中提取元素,以便显示帖子。

  • App.js
import { useGetPosts } from "../useRequest"
import Post from "../components/Post"

export default function IndexPage() {
  const { posts, error } = useGetPosts("/posts")

  if (error) return <h1>Something went wrong!</h1>
  if (!posts) return <h1>Loading...</h1>

  return (
    <div className="container">
      <h1>My Posts</h1>
      {posts.map(post => (
        <Post post={post} key={post.id} />
      ))}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

首先,我们导入useGetPosts钩子函数,然后将路径作为参数传递给它以执行请求。它会返回posts要显示的内容和一个错误状态。

之后,我们使用该Post组件显示数据数组。如果发生任何错误,我们会使用errorSWR 提供的相应方法进行处理。

完成这一步后,我们可以检查浏览器中是否一切正常。为此,请在命令行界面 (CLI) 打开项目并运行以下命令:

  yarn dev
Enter fullscreen mode Exit fullscreen mode

或者为了npm

  npm run dev
Enter fullscreen mode Exit fullscreen mode

让我们在浏览器上访问一下http://localhost:3000

app-preview-1

太棒了!我们已经通过钩子函数成功从服务器获取了数据useSWR

正如我们之前所说,SWR 提供了另一个钩子,可以轻松地对数据进行分页。让我们用它来更新我们的应用程序useSWRInfinite

使用以下方式对数据进行分页:useSWRInfinite

虽然仍然可以使用useSWR钩子对数据进行分页,但我并不推荐这样useSWRInfinite做,因为它需要额外的代码,而 SWR 已经提供了分页功能。

  • useRequest.js
import { useSWRInfinite } from "swr"

const fetcher = url => fetch(url).then(res => res.json())
const baseUrl = "https://jsonplaceholder.typicode.com"

export const usePaginatePosts = path => {
  if (!path) {
    throw new Error("Path is required")
  }

  const url = baseUrl + path
  const PAGE_LIMIT = 5

  const { data, error, size, setSize } = useSWRInfinite(
    index => `${url}?_page=${index + 1}&_limit=${PAGE_LIMIT}`,
    fetcher
  )

  const posts = data ? [].concat(...data) : []
  const isLoadingInitialData = !data && !error
  const isLoadingMore =
    isLoadingInitialData ||
    (size > 0 && data && typeof data[size - 1] === "undefined")
  const isEmpty = data?.[0]?.length === 0
  const isReachingEnd =
    isEmpty || (data && data[data.length - 1]?.length < PAGE_LIMIT)

  return { posts, error, isLoadingMore, size, setSize, isReachingEnd }
}
Enter fullscreen mode Exit fullscreen mode

useSWRInfinite钩子函数需要一个返回请求键、一个函数和选项的函数作为参数fetcher。请求键(index)是 SWR 用来确定要检索的数据(页面)的依据。请求键的初始值为 0 0,因此每次请求时都需要将其递增11。URL 中要定义的第二个参数是PAGE_LIMIT,它表示每次请求要获取的项目数。

useSWRInfinite返回的值比这多。我删除了这里不需要的数据。下面解释一下这些变量的作用:

  • posts是从服务器获取的数据数组。
  • isLoadingInitialData检查是否还有数据需要检索。
  • isLoadingMore检查我们当前是否正在检索数据。
  • isEmpty检查数据数组是否为空。
  • isReachingEnd检查是否达到页面限制。

接下来,我们将这些值返回给我们,以便在我们的组件中使用它们。

  • App.js
import { usePaginatePosts } from "../useRequest"

import Post from "../components/Post"

export default function IndexPage() {
  const {
    posts,
    error,
    isLoadingMore,
    size,
    setSize,
    isReachingEnd,
  } = usePaginatePosts("/posts")

  if (error) return <h1>Something went wrong!</h1>
  if (!posts) return <h1>Loading...</h1>

  return (
    <div className="container">
      <h1>My Posts with useSWRInfinite</h1>
      {posts.map(post => (
        <Post post={post} key={post.id} />
      ))}
      <button
        disabled={isLoadingMore || isReachingEnd}
        onClick={() => setSize(size + 1)}
      >
        {isLoadingMore
          ? "Loading..."
          : isReachingEnd
          ? "No more posts"
          : "Load more"}
      </button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

首先,我们导入usePaginatePostsAPI 端点并将其作为参数传递。接下来,我们使用钩子函数返回的值来显示帖子并加载新数据。load more点击按钮后,SWR 会将请求发送到下一页并返回数据。这样,数据就通过useSWRInfinite钩子函数实现了分页。

通过这一步,我们可以通过在命令行界面运行以下命令来测试分页功能是否正常工作:

  yarn dev
Enter fullscreen mode Exit fullscreen mode

让我们在浏览器上访问一下http://localhost:3000

app-preview-2
就这样!我们的应用看起来不错!

我们已经完成了在客户端使用 Next.js 实现 SWR 库的工作。您可以在这个CodeSandbox上找到最终项目。

你可以在我的博客上找到更多类似的精彩内容,或者在推特上关注我以获取通知。

感谢阅读!

文章来源:https://dev.to/ibrahima92/data-fetching-in-next-js-how-to-use-swr-34gn