如何创建自己的 React 自定义 Hooks

2025-06-08

如何创建自己的 React 自定义 Hooks

React 提供了许多可立即使用的内置钩子。除此之外,您还可以创建自己的自定义钩子。在本教程中,您将了解什么是 React 自定义钩子以及如何创建自己的钩子。您还将了解创建自定义钩子时需要遵循的规则。

React Hooks 的简要概述

React 团队在React v16.8中引入了 React Hooks。从那时起,Hooks 在 React 开发者乃至更广泛的开发者群体中迅速流行起来。在此之前,如果你想在 React 组件中使用状态和生命周期方法,你必须使用JavaScript 类

React Hooks 改变了这种模式。有了 Hooks,你不再需要为了使用状态而创建带有类的组件。你也可以创建函数式组件。然后,你可以使用 Hooks 来“增强”这些组件,添加任何你需要的功能,无论是状态、生命周期方法还是其他什么。

“钩子”这个词听起来可能有点模糊。为了更容易理解,可以简单地将钩子视为函数。这就是钩子,纯粹的JavaScript 函数。这些函数允许你“钩住”各种 React 特性,例如状态和生命周期。最常见的例子是useStateuseEffect钩子。

useState 钩子允许你将状态引入函数组件。使用 useEffect 钩子,你可以处理组件的生命周期。有一些 React钩子可供使用。然而,这些钩子并非万能,无法覆盖所有可能的场景。幸运的是,这些钩子并非唯一的选择。

React 自定义 Hooks 简介

除了发布了一系列官方的 React Hooks 之外,React 团队还提供了创建 React 自定义 Hooks 的功能。因此,如果您找不到可以解决某些问题的 Hooks,可以自行创建。您还可以使用 React 自定义 Hooks 来提升包含状态逻辑的代码的可复用性。

正如我们之前讨论过的,Hooks 本质上就是普通的 JavaScript 函数。区别在于,Hooks 有特定的用途。另一个区别在于,React Hooks 允许你使用其他 React Hooks,例如 useState,甚至其他自定义 Hooks。所以,不必担心创建自定义 Hooks 会很困难。

创建自定义钩子与编写常规 JavaScript 函数类似。您可能很快就能掌握。如果您知道如何使用 useState 和 useEffect 之类的钩子,速度会更快,因为您很可能会在自定义钩子中用到它们。但在深入探讨之前,我们需要学习一些规则。

钩子的规则

在创建第一个自定义钩子之前,您应该了解两条规则。这些规则称为钩子规则。首先,您只能在顶层调用钩子。切勿在嵌套函数、条件或循环内调用钩子。如果您想使用条件或循环,请在钩子内部使用它们,而不是反过来。

第二条规则是,你应该只从 React 函数组件或其他 hooks 中调用 hooks。创建自定义 hooks 也有一个惯例:始终以“use”前缀开头。这更像是一条经验法则,而不是一条规则。遵循这条规则可以使代码更具可读性,但并非强制要求。

构建你自己的 React 自定义 hooks

React 自定义钩子是 JavaScript 函数。这意味着几件事。首先,当你创建自定义钩子时,你实际上是在编写一个函数。其次,函数名称应该以“use”开头。记住,这是创建自定义钩子的一个好经验法则。第三,你可以在自定义钩子中使用其他 React 钩子。

这些是需要记住的事情。为了让这些内容更易于实践,我们来整理一下,并创建几个 React 自定义 hooks 的示例。这样可​​以更容易地理解自定义 hooks 的工作原理以及如何创建它们。

示例 1:useWindowSize 钩子

第一个例子是一个返回当前窗口大小的钩子。首先,定义一个名称。该名称应该具有描述性,并以“use”开头。例如,“useWindowSize”就比较合适。其次,定义一个钩子的逻辑。调用这个钩子时,它会执行一些操作。

它首先会获取当前窗口大小并返回。其次,它会将事件监听器附加到window对象并监听resize事件。当此事件发生时,钩子会检测新的窗口大小并再次返回。每次事件resize发生时都会重复此操作。

自定义钩子可以使用其他 React 钩子。这意味着我们可以使用 useState 钩子存储某个状态中最新的窗口尺寸并返回该状态的值。我们还可以使用 useEffect 钩子为resize事件添加事件监听器。我们可以使用 useEffect 钩子移除事件监听器。

我们可以通过返回一个清理函数来实现这一点。该函数将调用该removeEventListener方法,并传递resize事件和处理调整大小的函数。

// Import useEffect and useState hooks from React:
import { useEffect, useState } from 'react'

// Create custom useWindowSize hook function:
export function useWindowSize() {
  // Create function to get current window size:
  const getWindowSize = () => ({
    innerHeight: window.innerHeight,
    innerWidth: window.innerWidth,
    outerHeight: window.outerHeight,
    outerWidth: window.outerWidth,
  })

  // Create state for window size data:
  const [windowSize, setWindowSize] = useState(getWindowSize())
  // It also uses the getWindowSize() to set the initial state.

  // Create handler function for resize event:
  function handleResize() {
    // Update state value:
    setWindowSize(getWindowSize())
  }

  // Create a side-effect
  useEffect(() => {
    // Attach event listener for "resize" event:
    window.addEventListener('resize', handleResize)

    return () => {
      // Remove event listener for "resize" event:
      window.removeEventListener('resize', handleResize)
    }
  }, [])

  // Return current window size:
  return windowSize
}
Enter fullscreen mode Exit fullscreen mode

当你想要使用这个钩子时,将它导入到你的 React 组件中并调用它。记得将该调用赋值给一个变量,这样每次窗口大小发生变化时你都可以获取它。

// Import the useWindowSize hook:
import { useWindowSize } from './hook'

export default function App() {
  // Call the useWindowSize hook and assign it to variable:
  const windowSize = useWindowSize()

  // Display the current window size:
  return (
    <div>
      <ul>
        <li>window inner width: {windowSize.innerWidth}</li>
        <li>window inner height: {windowSize.innerHeight}</li>
        <li>window outer width: {windowSize.outerWidth}</li>
        <li>window outer height: {windowSize.outerHeight}</li>
      </ul>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

示例 2:useToggle 钩子

另一个简单但实​​用的钩子是用于管理切换状态的钩子。例如,这样的钩子对于创建可折叠组件非常有用。它可以帮助您检查当前的切换状态并在“开”和“关”状态之间切换。它还可以重置状态或手动设置状态。

这个钩子很简单。它将使用 useState 钩子来存储切换状态。除此之外,它还有两个函数:handleResethandleTogglehandleReset会将切换状态重置为初始值。handleToggle会反转当前的切换状态。它会从“开”切换到“关”,反之亦然。

我们将从此钩子返回一个对象。该对象将包含所有这些方法以及切换状态的当前值。我们还将返回状态的 setter 方法,以便设置自定义状态。导入此钩子后,您将能够在其返回的对象中导入任何内容。

// Import useEffect and useState hooks from React:
import { useState } from 'react'

// Create custom useToggle hook function:
export function useToggle(initialState = false) {
  // Create toggle state:
  const [toggle, setToggle] = useState(initialState)

  // Create handler function for resetting the state:
  const handleReset = () => setToggle(initialState)

  // Create handler function for toggling the state:
  const handleToggle = () => setToggle(prevState => !prevState)

  // Return the state, state setter function and handler functions:
  return {
    on: toggle,
    set: setToggle,
    reset: handleReset,
    toggle: handleToggle
  }
}
Enter fullscreen mode Exit fullscreen mode

与上一个钩子一样,你现在可以在 React 组件中导入这个 useToggle 了。调用它时,你可以使用解构赋值,将此钩子返回的对象中的任何内容赋值给一个变量,以便使用。

// Import the useToggle hook:
import { useToggle } from './hook'

export default function App() {
  // Call the useToggle hook and assign variables,
  // using destructuring assignment:
  const { on, set, reset, toggle } = useToggle()

  // Use any method or state returned from the hook:
  return (
    <div>
      <p>On: {on ? 'true' : 'false'}</p>

      <button onClick={() => set(true)}>Set to on</button>
      <button onClick={reset}>Reset</button>
      <button onClick={toggle}>Toggle</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

示例 3:useLocalStorage 钩子

第三个也是最后一个例子。将应用程序或网站数据存储在本地或会话存储中变得非常流行。此钩子接受两个参数:要存储的键的名称和该键的初始值。调用时,此钩子将首先检查浏览器中是否有可用的本地存储。

如果本地存储不可用,它将返回作为参数传递的初始值。如果本地存储可用,它将检查是否存在同名的键。如果存在,它将检索其数据。否则,它将返回初始值。返回的任何值都将成为钩子的状态。

所有这些都会在初始加载期间发生。它会在 useState hook 的初始化函数内部进行。该 hook 的第二部分是一个用于将数据存储到本地存储中的处理函数。该函数将接受一个参数,即要存储的数据。它首先会获取该值并将其存储在 hook 状态中。

然后,它会将值存储在本地存储中。此数据的键名将是调用期间传递给钩子的键名。最后一部分,返回一些值。此钩子将返回两项:状态的当前值、从本地存储加载的数据以及用于将数据存储到本地存储的处理函数。

// Import useState hook from 'react':
import { useState } from 'react'

export function useLocalStorage(keyName, initialValue) {
  // Create state for local storage:
  const [storedValue, setStoredValue] = useState(() => {
    try {
      // Check if local storage is available:
      if (typeof window === 'undefined') {
        return initialValue
      }

      // Check if item with the same name exists in local storage:
      const item = window.localStorage.getItem(keyName)

      // Return parsed data from storage or return the initialValue:
      return item !== null ? JSON.parse(item) : initialValue;
    } catch (error) {
      // Catch any errors and log them:
      console.log(error)

      // Return the initialValue:
      return initialValue
    }
  })

  // Create handler function for storing value in local storage:
  const setValue = (value) => {
    try {
      // Store the value in the state:
      setStoredValue(value)

      // Store the value in local storage:
      window.localStorage.setItem(keyName, JSON.stringify(value))
    } catch (error) {
      // Catch any errors and log them:
      console.log(error)
    }
  }

  // Return latest data and handler function for storing new data:
  return [storedValue, setValue]
}
Enter fullscreen mode Exit fullscreen mode

此钩子的使用方法与 useState 类似。调用时,传入键的名称和该键对应的数据。钩子将返回一个包含数据的数组,以及用于存储新数据的处理函数。返回的数据可以是初始值,也可以是该键已存储在本地存储中的任何数据。

// Import the useLocalStorage hook:
import { useLocalStorage } from './hook'

export default function App() {
  // Call the useLocalStorage hook and assign variables,
  // again, using destructuring assignment:
  const [value, setValue] = useLocalStorage('name', 'Joe')

  // Store data typed in the input in local storage
  // and also display them in the DOM:
  return (
    <div>
      <p>{value}</p>

      <input type="text" onChange={(e) => setValue(e.currentTarget.value)} />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

结论:如何创建自己的 React 自定义 Hooks

官方的 React Hooks 对每个 React 开发者来说都是非常有用的工具。然而,这些官方 Hooks 并不能满足你的所有需求。编写你自己的 React 自定义 Hooks 可以帮助你解决这个问题。我希望本教程能帮助你了解什么是 React 自定义 Hooks、它们的工作原理以及如何创建你自己的自定义 Hooks。

鏂囩珷鏉ユ簮锛�https://dev.to/alexdevero/how-to-create-your-own-react-custom-hooks-19d1
PREV
如何通过三种方式编写异步 JavaScript 代码
NEXT
JavaScript Fetch API 入门