在 React 中构建自定义钩子来获取数据

2025-05-26

在 React 中构建自定义钩子来获取数据

从后端获取数据是 Web 应用程序的关键部分之一。每个应用程序要想动态运行,都需要从服务器获取数据,然后将其显示在用户界面上。

我们使用 API 调用检索数据,并使用内置的 React hooks(例如 useState、useEffect 和 useReducer)将检索到的数据设置为状态变量。然后在组件中使用它来在视图中显示它。

在深入了解代码如何实现之前,让我们先了解一下什么是 React hooks 以及为什么要使用它。

什么是 React Hooks

React Hooks 最早是在 React 16.8 中引入的。它们是一些可以让你钩住 React 状态的函数。

React 提供的一些内置钩子是 useState、useEffect、useContext、useReducer、useRef、useCallback 和 useMemo。

为什么使用 React Hooks

使用 React Hooks 的主要优势之一是逻辑的可重用性。Hooks 可以在需要使用特定功能的多个组件中使用。

它还使代码更具可读性、效率和易于维护。

从服务器获取数据并在组件中更新的正常代码如下所示



export function Home(){

    const [data,setData] = useState(null)
    const [loading,setLoading] = useState(false)
    const [error,setError] = useState(null)

    useEffect(()=>{
        (
        async function(){
            try{
                setLoading(true)
                const response = await axios.get('http:localhost:4000')
                setData(response.data)
            }
            catch(err){
                setError(err)
            }finally{
                setLoading(false)
            }
        }
        )()
    },[])

    return(
        {loading && <div>Loading...</div>}
        {data && <div>{data}</div>
    )
}


Enter fullscreen mode Exit fullscreen mode

我们在 useEffect 钩子内编写逻辑来更新状态属性,如数据、加载和错误。

虽然这样写完全没问题,但是如果我们想在多个组件中做同样的事情,并且需要获取其他数据,该怎么办呢?

我们必须在所有这些组件中多次重写所有这些代码,这不是很高效并且难以管理。

在大型代码库中,最好遵循“不要重复自己”(DRY)原则,也就是说,最好编写一次代码并使其可重复使用,而不是在多个组件中一遍又一遍地编写它。

这就是自定义钩子的真正魔力所在。我们可以将代码写在一个单独的 js 文件中,然后所有需要从服务器获取数据的组件都可以使用 URL 来调用它。

这使得代码高效且易于维护。

就像 useState 和 useEffect 有其功能一样,我们通过组合它们来创建自定义钩子以实现特定的能力。

创建自定义 useFetch 钩子

我们首先创建一个名为 useFetch.js 的新 JavaScript 文件。
根据 React hooks 的约定,钩子的名称以 use 开头。

useFetch javascript 文件

在文件中,创建一个以钩子名称命名的新函数。React 钩子和 React 组件的区别在于,钩子不返回 JSX。它只返回你想在组件中使用的状态变量或函数。



export function useFetch(){

}


Enter fullscreen mode Exit fullscreen mode

要进行 API 调用,请使用 useEffect hook,因为它会在渲染时触发其内部的 API 调用函数。此处,API 调用是使用 Axios 进行的。

需要调用的 API Url 作为参数从组件传递给钩子。



import { useEffect } from "react"
import axios from axios

export function useFetch(url){
   useEffect(()=>{
      (
         async function(){
            const response = await axios.get(url)
         }
      )()
   },[url])

}


Enter fullscreen mode Exit fullscreen mode

通常,我们有 3 个状态变量,即使用 useState 创建的数据、错误和加载,分别用于存储响应数据、错误和加载,

如果接收到数据,我们将其设置为 data 变量。如果没有接收到,则将错误消息设置为 error 变量。

Loader 初始化为 false,调用 API 时会设置为 true,这样视图中就可以加载一个 loader 组件。

在 API 调用结束时,使用 finally 块将此加载器设置回 false。



import { useEffect, useState } from "react"
import axios from "axios"


export default function useFetch(url){

    const [data,setData] = useState(null)
    const [error,setError] = useState(null)
    const [loading,setLoading] = useState(false)

    useEffect(() => {
        (
            async function(){
                try{
                    setLoading(true)
                    const response = await axios.get(url)
                    setData(response.data)
                }catch(err){
                    setError(err)
                }finally{
                    setLoading(false)
                }
            }
        )()
    }, [url])

    return { data, error, loading }

}


Enter fullscreen mode Exit fullscreen mode

我们要放入 useEffect 依赖数组的唯一依赖项是 Url,因为如果 Url 发生变化,我们就必须请求新数据。

这基本上就是 useEffect 的用法。现在我们将 hook 内部创建的状态以对象的形式返回。

在组件中使用自定义钩子

在组件内部,从其 JavaScript 文件导入 useFetch 钩子。导入后,使用 API Url 作为参数调用该钩子。

使用自定义 Hook 获取数据



export function Home(){
    const {data,loading,error} = useFetch('https://localhost:4000')

        if(error){
           console.log(error)
        }

    return(
        {loading && <div>Loading...</div>}
        {data && <div>{data.map(item => <div>{item}</div>)}</div>}
    )
}


Enter fullscreen mode Exit fullscreen mode

除此之外,我们还可以通过让钩子返回可以从组件调用的任何函数来定制钩子。

例如,我们可以在钩子内部创建一个 refetch() 函数,当调用时重新获取 API。

该函数可以从钩子返回,并且可以从组件调用。

文章来源:https://dev.to/shaedrizwan/building-custom-hooks-in-react-to-fetch-data-4ig6
PREV
已移除对密码验证的支持。请改用个人访问令牌。
NEXT
HTML5 赋予你的 10 大超能力(而你尚未使用)