构建自定义 React Hooks

2025-06-07

构建自定义 React Hooks

React Hooks 简化了创建可重用、简洁且通用的代码的过程,并且像 memoization 这样的高级优化技术现在也更易于访问和使用。React 的官方文档只涵盖了基本 Hooks,并未详细介绍自定义 Hooks,因此本文将主要关注构建自定义 React Hooks 及其最佳实践。

要充分利用本文,您需要了解 React Hooks 的基本知识。如果您还不熟悉这些基础知识,可以参考许多优秀的文章。例如,React 官方文档就是一个不错的起点。

心态

为了构建一个多功能、高性能和可重用的自定义钩子,需要记住几件事。

每次组件重新渲染时都会运行钩子

由于我们使用的是函数式组件和钩子,我们不再需要生命周期方法。每次状态或属性发生变化时,函数式组件都会重新渲染,因此,我们的自定义钩子会被一遍又一遍地调用。

尽可能使用基本钩子

基本 React Hook 是任何自定义 Hook 的核心。我们可以使用memoizationHook 依赖数组来控制自定义 Hook 的哪些部分会在每次重新渲染时更改,哪些部分不会更改。为了有效地使用它们并构建高性能 Hook,了解每个基本 Hook 在自定义 Hook 中的作用至关重要。

钩子的规则

有一些重要的规则需要牢记。这些规则在React hooks 文档中有详细的解释。

构建自定义钩子

既然我们已经了解了基础知识,现在就可以构建自己的自定义钩子了。在下面的示例中,我们将建立一个构建自定义钩子的可靠模式,并介绍一些最佳实践。

假设我们正在开发一个项目,用户可以玩多个以掷骰子为游戏机制的游戏。有些游戏只需要一个骰子,而有些游戏可能需要多个骰子。我们还假设在某些游戏中,使用的骰子数量可能会发生变化。

牢记这一点,我们将构建具有以下功能的useGameDice钩子:

  • 自定义钩子可以用正在使用的骰子数量和初始值进行初始化
  • 设置所用骰子数量的函数
  • 掷骰子的函数。返回一个 1 到 6 之间的随机数数组。长度由使用的骰子数量决定。
  • 将所有骰子值重置为初始值的函数

设置钩子(导入和钩子函数)

我们将自定义钩子声明为常规箭头函数,遵循自定义钩子的命名规范——名称应以“use”关键字开头。我们还导入了稍后将在实现中使用的 React 钩子。我们还可以导入常量、其他函数、其他自定义钩子等。

我们的钩子可以用两个可选变量来初始化:

  • initialNumberOfDice - 将使用多少个骰子
  • initialDiceValue - 确定初始值和重置后的值

这两个变量的默认值都是 1,以避免任何错误并简化钩子设置。

import { useState, useMemo, useCallback, useEffect } from "react";

export const useGameDice = (initialNumberOfDice = 1, initialDiceValue = 1) => {
 /* We'll be adding code here in order */
};
Enter fullscreen mode Exit fullscreen mode

添加状态和记忆私有变量

首先,我们需要设置状态。我们将声明两个简单的状态:

  • diceValue - 数组,其大小由 numberOfDice 定义,并保存每个骰子的值
  • numberOfDice - 确定将使用的骰子数量(diceValue 数组大小)

我们还初始化了initialDiceState变量,该变量创建了初始数组值,该值将在首次渲染和状态重置时分配。此值会被记忆,以避免在每次重新渲染时初始化数组并填充默认值。

 const [diceValue, setDiceValue] = useState();
 const [numberOfDice, setNumberOfDice] = useState(initialNumberOfDice);

 const initalDiceState = useMemo(
   () => Array(numberOfDice).fill(initialDiceValue),
   [numberOfDice, initialDiceValue]
 );
Enter fullscreen mode Exit fullscreen mode

添加记忆钩子函数

接下来,我们将创建以下函数:

  • generateRandomDiceNumber - 生成 1 到 6 之间的随机数(一次掷骰子)
  • rollDice - 为数组中的每个元素(骰子)调用随机数生成器
  • resetDice - 将骰子值状态重置为初始值
const generateRandomDiceNumber = useCallback(() => {
   return Math.floor(Math.random() * 6) + 1;
}, []);

const rollDice = useCallback(() => {
   const arrayConfig = { length: numberOfDice };
   const newDiceValues = Array.from(arrayConfig, generateRandomDiceNumber);
   setDiceValue(newDiceValues);
}, [numberOfDice, generateRandomDiceNumber]);

const resetDice = useCallback(() => {
   setDiceValue(initalDiceState);
}, [initalDiceState]);
Enter fullscreen mode Exit fullscreen mode

我们使用useCallback hook 来控制函数何时重新初始化。只有当依赖数组中的任何变量发生变化时,函数才会重新初始化。以generateRandomDiceNumber函数为例,它在首次渲染和初始化后永远不会重新初始化,因为该函数不依赖于任何外部变量或状态。

添加副作用 - 钩子初始化和更新

我们需要设置一个监听器来监视骰子初始状态的更新。这个副作用有两个作用:

  1. 当钩子首次初始化时,它将骰子状态设置为初始值
  2. 当骰子数量(数组大小)发生变化时,它将骰子状态更新为初始值
 useEffect(() => {
   setDiceValue(initalDiceState);
 }, [initalDiceState]);
Enter fullscreen mode Exit fullscreen mode

API 设置和返回语句

最后,我们定义 state 和 api 对象,并按照 useState 的约定将它们返回到一个数组中。我们来看看每个对象:

  • state - 保存所有状态值。我们预计此对象几乎每次重新渲染时都会发生变化
  • api - 包含所有函数。我们将返回useCallback中声明的一些函数以及useState hook中的一个函数。此对象被记录下来,因为我们预计它不会在每次重新渲染时发生变化。
const state = {
   diceValue,
   numberOfDice
 };

const api = useMemo(
   () => ({
     setNumberOfDice,
     rollDice,
     resetDice
   }),
   [setNumberOfDice, rollDice, resetDice]
 );

 return [state, api];
Enter fullscreen mode Exit fullscreen mode

我们将对象以数组的形式返回,因为我们希望这个钩子更加灵活。这样,我们允许开发人员重命名返回的变量,并允许他们在需要时初始化此钩子的多个实例。

 const [diceFirst_state, diceFirst_api] = useGameDice();
 const [diceSecond_state, diceSecond_api] = useGameDice();
Enter fullscreen mode Exit fullscreen mode

Git 存储库和演示

您可以在以下GitHub 存储库中查看最终实现和完整代码以及演示

替代文本

React 自定义 hooks 模式概述

现在,你可能已经注意到,我们将要添加的代码按部分分组了。这种结构清晰的模式遵循以下逻辑路径:

  1. 状态初始化(useState、useReducer)、局部变量初始化(useMemo)、ref 初始化(useRef)和外部自定义 hooks 初始化
  2. 记忆函数(useCallback)
  3. 副作用(useEffect)
  4. API 设置(状态和记忆 API)
  5. Return 语句

结论

Hooks 受到 React 社区的热烈欢迎并不令人意外。开发者能够更轻松地在组件之间共享逻辑,为每个自定义 Hook 创建多个组件(接口),并挑选 Hook 的状态和 API 部分用于组件中,等等。

这种可重用性和多功能性使 Hooks 成为 React 应用开发中真正的颠覆者。在构建自定义 React Hooks 时,凭借既定的模式和最佳实践,开发人员能够交付质量一致、结构清晰且性能最佳的代码。


这些文章都是咖啡带来的灵感。所以,如果你喜欢我的文章,觉得它有用,不妨请我喝杯咖啡!我会非常感激的。

给我买杯咖啡

感谢您花时间阅读这篇文章。如果您觉得它有用,请点赞 ❤️ 或 🦄,分享并评论。

本文也可在Medium上阅读,如果您喜欢,请随意给它一个👏。

文章来源:https://dev.to/prototyp/building-custom-react-hooks-11h2
PREV
将 Git 变成应用程序数据库
NEXT
避免混乱的 git 历史记录 避免混乱的 git 历史记录