React 自定义 hooks:简单解释

2025-06-07

React 自定义 hooks:简单解释

大家好👋

React 16.8 V 带来了一些实用功能,其中之一就是 Hooks。Hooks 非常实用。一些预定义的 Hooks 包括 useState 和useEffect。今天我们来学习如何编写自定义 Hooks 🎣。

但在开始之前,我们先来了解一下为什么需要编写自定义 hook?
一个常见的场景是,我们需要将重复的代码/逻辑提取出来,并将其拆分成一个函数,这样在需要的时候就可以重复使用相同的代码。
传统上,在 React 中共享状态逻辑有两种常用方法。

  • 渲染道具或
  • 高阶组件(HOC)。

但有了 Hooks,我们就有了更多的灵活性和便捷性。React 定义自定义 Hook 如下:

名称以“use”开头的 JavaScript 函数,可以调用其他 Hook。

在我们编写自定义钩子之前,让我们先看看钩子的规则。

钩子的规则

基本上,使用钩子时我们有两条规则,它们是:

  • 仅在顶层调用 Hooks
  • 仅从 React 函数调用 Hooks

第一条规则是,不要在条件中使用钩子,因为 React 依赖于钩子的调用顺序。

第二条规则是说只使用来自反应函数的钩子或在自定义钩子中使用钩子。

我们将在另一篇文章中深入介绍钩子的规则,但在编写自定义钩子时请务必记住这些要点。另外,请务必使用“ use ”命名钩子。

编写一个简单的自定义钩子✍

假设我们有以下功能组件,它显示一个用户名,并使用 axios get 调用从后端获取。Axios 只是一个允许我们进行 api 调用的库。数据获取是使用 useEffect 钩子实现的,该钩子在组件挂载时执行 axios 请求。请注意,为了便于理解,我没有使用清理函数,但在理想情况下,我们必须使用清理函数。我已经在另一篇关于useEffect 的文章中解释了清理函数的重要性。以下代码触发 axios get 请求来获取用户名。它还会在执行期间/之后呈现加载消息或错误消息。

export default function DisplayUserName() {

  const [userName, setUserName] = useState(null);
  const [loading,setLoading] = useState(false);
  const [error,setError] = useState(null);
  //Runs on mounting of the DisplayUserName component
  useEffect(() => {
    setLoading(true);
    axios
      .get('http:localhost:5000/getusername')
      .then((res) => {
          setUserName(res.data);
          setLoading(false);
      })
      .catch((err) => {
            setLoading(false);
            setError(err);
        });
  }, []);

  return (
    <div className="App">
      {loading ? "Loading ..." : <h1> Username : {userName} </h1>}
      {error && <h2> {error} </h2>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

上面的代码运行良好。但是,一个正常的应用程序会从不同的组件中调用大量的 API,显然我们可能需要在每个组件中重复相同的逻辑。因此,为了避免这些重复的逻辑,我们可以将这些通用逻辑分离到我们自己的自定义钩子中,该钩子返回包含响应、错误和加载状态的对象,如下所示。

export const useGetQuery = (url) => {
  const [response, setResponse] = useState(null);
  const [loading,setLoading] = useState(false);
  const [error,setError] = useState(null);
  // Runs whenever the dependency url changes
  useEffect(() => {
    setLoading(true);
    axios
      .get(url)
      .then((res) => {
        setResponse(res.data);
        setLoading(false);
      })
      .catch((err) => {
        setLoading(false);
        setError(err);
      })
  }, [url]);

  return { response,loading,error };
};
Enter fullscreen mode Exit fullscreen mode

在这里,我们提取代码逻辑并将其保存在一个单独的函数中。请注意,我们的自定义钩子名称以 use 开头。这样做是为了让 React 理解这是一个钩子,并针对我们的代码显示相应的警告或错误。React 强烈建议我们遵循相同的约定。另请注意,返回对象包含响应、加载和错误值。这些值可用于任何使用我们新的自定义钩子的组件。以下代码使用我们的自定义钩子在适用的情况下显示用户名、错误和加载消息。

export const DisplayUserName = () => {
     const url = 'http:localhost:5000/getusername';
     const {response,loading,error} = useGetQuery(url);
    return (
    <div className="App">
      {loading ? "Loading ..." : <h1> Username : {response} </h1>}
      {error && <h2> {error} </h2>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

使用自定义钩子时的常见错误🐞

看到这个错误了吗?

无效的钩子调用。钩子只能在函数组件的主体内调用。以下原因可能导致此问题:1. 你的 React 版本和渲染器版本不匹配(例如 React DOM);2. 你可能违反了钩子规则;3. 你可能在同一个应用中安装了多个 React 版本。请参阅https://reactjs.org/link/invalid-hook-call获取有关如何调试和修复此问题的提示。

或者这个?

React Hook 的“useGetQuery”不能在回调函数中调用。必须在 React 函数组件或自定义 React Hook 函数中调用。

或者这个?

React Hook“useGetQuery”在函数“handleClick”中被调用,该函数既不是React函数组件也不是自定义React Hook函数。

当开发人员尝试在回调中调用自定义钩子时,可能会出现此问题。还记得我一开始告诉你的规则吗?这个错误说明你违反了“仅在 React 函数式组件中使用钩子”的规则,并且禁止在其他任何地方使用它们。

回调中的自定义钩子

根据钩子的规则,我们不能在条件或回调中使用它们。但是如果我们必须在回调中使用逻辑呢?
下面给出了一个快速解决方法。

export const useFetchQuery = (time) => {
  const [response, setResponse] = useState(null);
  const [loading, setLoading] = useState(false);
  const fruits = [🍎, 🍌, 🥭, 🍇, 🍉];
  const fetchDetails = (time) => {
    setLoading(true);
    setResponse(null);
    //Logic to update the response to a random fruit
    setTimeout(() => {
      setResponse(fruits[Math.floor(Math.random() * 10) % 4]);
      setLoading(false);
    }, time);
  };
  //The fetchDetails method is returned from our custom hook
  return { fetchDetails, response, loading };
};

Enter fullscreen mode Exit fullscreen mode

上面的代码很容易理解。我使用了 setTimeout 函数来模拟 API 调用。fetchDetails 函数会从水果数组中随机取出一个水果,并更新响应。它还会更新加载状态。

注意我们如何从自定义钩子 useFetchQuery 返回 fetchDetails 函数。此函数可以在我们的回调中使用,如下所示。

  const { fetchDetails, response, loading } = useFetchQuery(2000);

  const handleClick = () => {
    //Notice the fetchDetails method which is used below
    fetchDetails(2000);
  };
  return (
    <div className="App">
      <button onClick={handleClick}> Click Here </button>
      {loading && <h1>Loading ...</h1>}
      {response && <h1>Random Fruit : {response}</h1>}
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

单击按钮时,将执行 handleClick 回调,进而从我们的自定义钩子中调用 fetchDetails 函数。

这是代码和框:

结论

React 提供了多种 hooks。开发者可以根据需要随时编写自定义 hook,从而拥有更大的灵活性。编写自定义 hook 时,请务必牢记 hooks 的规则。希望您已经掌握了创建自定义 hook 的基础知识。欢迎关注更多类似文章。下次再见🤟

文章来源:https://dev.to/_ali_/react-custom-hooks-a-simple-explanation-bpj
PREV
像布置房子一样布置代码
NEXT
JavaScript 测试入门