React:创建用于获取数据的自定义钩子

2025-06-04

React:创建用于获取数据的自定义钩子

从外部或内部 API 获取数据是 Web 应用程序的常见用例。React 函数式组件提供了不同的钩子来获取数据。本文将解释这些钩子,并帮助您了解何时使用它们。

上下文:获取用户的棋盘游戏收藏

在我的应用中,我想读取用户的棋盘游戏收藏并进行渲染。BoardGameGeek 平台提供了一个 JSON API。以下是示例:

curl https://bgg-json.azurewebsites.net/collection/newuser
[
  {
    "gameId": 180263,
    "name": "The 7th Continent",
    "image": "https://cf.geekdo-images.com/original/img/iQDBaRJ2LxJba_M7gPZj24eHwBc=/0x0/pic2648303.jpg",
    "thumbnail": "https://cf.geekdo-images.com/thumb/img/zj6guxkAq2hrtEbLGFrIPCh4jv0=/fit-in/200x150/pic2648303.jpg",
    [...]
  }
]
Enter fullscreen mode Exit fullscreen mode

要求

在开始编写代码之前,我喜欢花一些时间思考需求。这样,你就能有一个大致的框架和一个用于评估实现进度的清单。

让我们集思广益。获取数据是一个需要未知时间的过程。因此,我们应该给这个过程设置一个超时时间,并跟踪加载状态。获取数据可能会产生不同的错误:它可能完全失败,或者数据集可能与我们预期的不同,或者数据集本身存在错误。我们应该处理这些错误情况,并将错误视为获取过程的最终状态。

基本要求如下:

  • R1 应该可以配置urltimeout
  • R2 它应该返回 的状态loadingerror以及result

基本实现

基本要求可以通过以下代码来满足:

1 import React, {useState} from 'react';
2
3 function useFetchData(url, timeout) {
4   const [data, setData] = useState([]);
5   const [loading, setLoading] = useState(false);
6   const [error, setError] = useState(false);
7
8   return {data, loading, error};
9 }
Enter fullscreen mode Exit fullscreen mode
  • 在第 3 行中,我们定义了useFetchData函数,构造函数根据自定义钩子约定命名,并接收值urltimeout
  • 在第 4 至 6 行中,变量dataloadingerror是用useState钩子定义的
  • 在第 8 行,返回所有状态变量

现在我们需要实现所需的功能。

获取数据

让我们编写获取数据的函数。

1  async function load() {
2    setLoading(true);
3    try {
4      const result = await axios.fetch(url, {timeout: timeout}).data;
5      setData(result);
6    } catch (e) {
7      setError(true);
8    }
9    setLoading(false);
10 }
Enter fullscreen mode Exit fullscreen mode
  • 在第 2 行中,我们设置了loading = true,并且仅在该函数的末尾将其设置为false
  • 在第 3 行中,我们使用一个try … catch围绕实际 API 调用的块来捕获所有错误
  • 在第 4 行中,我们使用axios 库向 URL 发出实际请求,并提供timeout
  • 在第 5-7 行中,如果获取数据成功,我们将设置dataresult,如果不成功,我们将设置error = true

通过这种逻辑,我们确保数据提取始终具有明确定义的状态:正在加载,或者如果没有加载,则会产生结果或错误。

重构

这个钩子满足了我们的要求 R1 和 R2。我们还能改进什么呢?每当组件被调用时,我们都应该将其状态重置为初始值。

function init() {
  setData([]);
  setLoading(true);
  setLoading(false)
}

async function load() {
  init();
  ...
}
Enter fullscreen mode Exit fullscreen mode

如果我们在函数组件声明中直接调用该函数,会发生什么load?该函数会改变组件的状态,从而触发重新渲染,load并再次执行……

因此,需要从外部调用该函数 - 我们需要将其导出到使用此钩子的组件。

return {data, loading, error, load};
Enter fullscreen mode Exit fullscreen mode

最终组件

这是最后一个组件:

import React, {useState} from 'react';

function useFetchData(url, timeout) {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);

  function init() {
    setData([]);
    setLoading(true);
    setLoading(false)
  }

  async function load() {
    init();
    setLoading(true);
    try {
      const result = await axios.fetch(url, {timeout: timeout}).data;
      setData(result);
    } catch (e) {
      setError(true);
    }
    setLoading(false);
  }

return {data, loading, error, load};
}

export default useFetchData;
Enter fullscreen mode Exit fullscreen mode

结论

本文介绍了如何实现自定义数据获取钩子。我们了解到,组件始终需要保持一个精确的状态:正在加载,或者加载完成并返回结果或错误。当 API 被访问时,我们假设请求可能失败、数据未验证以及其他错误——所有这些都会被捕获并处理。最后,我们导出所有状态变量和加载函数,以便调用者拥有最大程度的控制权。

文章来源:https://dev.to/admantium/react-creating-a-custom-hook-for-fetching-data-168h
PREV
在 Laravel 中实现 UUID 主键,以及它的好处
NEXT
[不要落后]学习这些主题,让自己成为一名自信的 Web 开发者