在 React 中编写自己的 useFetch Hook 我们的 useFetch 函数签名 在 Hook 中维护状态 当我们的 URL 或选项更改时运行效果 使用 Async Await 调用 Fetch 使用 Hook 总结

2025-06-10

在 React 中编写自己的 useFetch Hook

我们的 useFetch 函数签名

在 Hook 中维护状态

当我们的 URL 或选项改变时运行效果

使用 Async Await 调用 Fetch

使用 Hook

结论

React Hooks 风靡一时,已经一年多了。让我们看看如何实现我们自己的useFetchHook,将获取请求逻辑从组件中抽象出来。

注意:这仅用于学术目的。您可以自行开发hook 并将其用于生产环境,但我强烈建议使用像use-http 这样useFetch的成熟库来帮您完成繁重的工作!


如果您喜欢这篇文章,请给它一个💓、🦄或🔖,并考虑订阅📬我的免费每周开发者新闻通讯


我们的 useFetch 函数签名

为了确定useFetch函数签名,我们应该考虑实际执行获取请求时可能需要从最终用户那里获取的信息。在这种情况下,我们会说我们需要资源url,以及options可能与请求相关的信息(例如,请求方法)。

function useFetch(initialUrl, initialOptions) {
  // Hook here
}
Enter fullscreen mode Exit fullscreen mode

在功能更齐全的解决方案中,我们可能会为用户提供一种中止请求的方法,但目前我们对这两个论点感到满意!

在 Hook 中维护状态

我们的钩子需要维护一些状态。我们至少需要维护urloptions的状态(因为我们需要为用户提供访问setUrl和 的方法setOptions)。此外,我们还需要一些其他状态变量!

  • 数据(从我们的请求返回的数据)
  • 错误(请求失败时出现的任何错误)
  • 加载(布尔值,指示我们是否正在主动获取)

让我们使用内置useState钩子创建一堆有状态变量。此外,我们还希望让用户有机会做以下事情:

  • 设置网址
  • 设置选项
  • 查看检索到的数据
  • 查看任何错误
  • 查看加载状态

因此,我们必须确保从我们的钩子返回这两个状态设置函数和三个数据!

import { useState } from 'React';

function useFetch(initialUrl, initialOptions) {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  // Some magic happens here

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

重要的是,我们将我们的urloptions默认为钩子首次调用时提供的initialUrlinitialOptions。此外,您可能会想,这些变量有很多不同的,您想将它们全部保存在同一个对象或几个对象中——这完全没问题!

当我们的 URL 或选项改变时运行效果

这部分非常重要!我们需要fetch在每次urloptions变量发生变化时执行一次请求。还有什么比内置useEffect钩子更好的方法吗?

import { useState } from 'React';

function useFetch(initialUrl, initialOptions) {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // Fetch here
  }, [url, options]);

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

使用 Async Await 调用 Fetch

比起 Promise 语法,我更喜欢 async/await 语法,所以就用前者吧!当然,用thencatch、 和 也一样好finally,比用 async/await 好用。

import { useState } from 'React';

function useFetch(initialUrl, initialOptions) {
  const [url, setUrl] = useState(initialUrl);
  const [options, setOptions] = useState(initialOptions);
  const [data, setData] = useState();
  const [error, setError] = useState();
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    setLoading(true);
    setError(undefined);

    async function fetchData() {
      try {
        const res = await fetch(url, options);
        const json = await res.json();
        setData(json);
      } catch (e) {
        setError(e);
      }
      setLoading(false);
    }
    fetchData();
  }, [url, options]);

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

真是太多了!我们稍微分解一下。运行效果时,我们知道开始获取数据了。因此,我们将loading变量设置为true,并清除之前可能存在的任何错误。

在异步函数中,我们fetch用一个try/catch块包装了请求代码。我们希望将遇到的任何错误报告给用户,因此在catch块中,我们setError会记录所有报告的错误。

在我们的代码块中try,我们执行了一个相当标准的fetch请求。我们假设返回数据是json因为我很懒,但如果我们要让它成为最通用的钩子,我们可能会给用户提供一种配置预期响应类型的方法。最后,假设一切成功,我们将data返回的 JSON 值赋给变量!

使用 Hook

信不信由你,这就是创建自定义钩子的全部步骤!现在我们只需要将它引入示例应用程序,并希望它能够正常工作。

在下面的示例中,我有一个应用可以加载任何 GitHub 用户的基本 GitHub 个人资料数据。除了设置fetch选项之外,这个应用几乎实现了我们为钩子设计的所有功能。我们可以看到,在加载 fetch 请求时,我们可以显示“正在加载”指示。当 fetch 完成时,我们会显示错误信息或结果的字符串化版本。

我们为用户提供了一种输入不同 GitHub 用户名来执行新请求的方式。提交后,我们会调用钩子setUrl导出的函数useFetch,执行 effect 并发起新的请求。很快就能拿到新数据了!

const makeUserUrl = user => `https://api.github.com/users/${user}`;

function App() {
  const { data, error, loading, setUrl } = useFetch(makeUserUrl('nas5w'));
  const [user, setUser] = useState('');

  return (
    <>
      <label htmlFor="user">Find user:</label>
      <br />
      <form
        onSubmit={e => {
          e.preventDefault();
          setUrl(makeUserUrl(user));
          setUser('');
        }}
      >
        <input
          id="user"
          value={user}
          onChange={e => {
            setUser(e.target.value);
          }}
        />
        <button>Find</button>
      </form>
      <p>{loading ? 'Loading...' : error?.message || JSON.stringify(data)}</p>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

请随意查看此处 codesandbox 上的useFetch钩子和示例应用程序

结论

编写自定义 React Hook 可能是一件很有趣的事情。一开始可能会有点棘手,但一旦掌握了窍门,就会发现它非常有趣,而且可以大大缩短组件代码,减少冗余。

如果您对这个钩子、React 或 JS 有任何疑问,请随时通过 Twitter 与我联系

鏂囩珷鏉ユ簮锛�https://dev.to/nas5w/writing-your-own-usefetch-hook-in-react-2i3g
PREV
60 行代码即可编写您的第一个 Deno 服务器 入门 指定域名 获取服务器库 添加路由 最后一步:日志中间件 Fin
NEXT
使用流畅接口模式创建 JavaScript 对象为什么要以这种方式构建对象?