React中useReducer钩子的使用指南

React中useReducer钩子的使用指南

前言

在这篇文章中,我们将深入探讨React中的useReducer钩子。如果你是第一次接触这个钩子,它可能看起来有些复杂。本文将把useReducer钩子的概念分解成易于理解的部分,并通过代码和实际例子来帮助你掌握其功能。

如果你正在理解useReducer的概念和工作原理方面遇到困难,那么这篇文章就是为你准备的。不过,理解状态如何工作的基础知识对于理解本文的内容至关重要。如果你已经熟悉了React状态的概念,那么就让我们开始探索useReducer的世界吧!

在进一步讨论之前,需要注意的是,useStateuseReducer钩子在某些方面是相似的。

useReduceruseState钩子的对比

  • 它们都涉及当前的状态值,都有一个触发状态更新的函数,并且都有作为参数传递的初始状态值。
  • useReducer是功能组件中管理状态的useState钩子的替代方案。useReducer钩子更适合管理复杂的状态逻辑,而useState最适合简单的状态变化。
  • 当状态逻辑变得过于复杂,或者当你需要以更可预测和可管理的方式处理状态变化时,useReducer钩子是你的最佳选择。

什么是useReducer

useReducer是React中的一个钩子,允许你在组件中添加reducer。它接收reducer函数和初始状态作为参数。useReducer还返回一个包含当前状态和dispatch函数的数组。

javascript 复制代码
const [state, dispatch] = useReducer(reducer, initialState);

让我们熟悉一下这些参数的含义:

  • state:表示当前值,在初始渲染期间设置为initialState值。
  • dispatch :一个更新状态值的函数,与useState中的更新函数一样,总是触发重新渲染。
  • reducer:一个包含所有状态更新逻辑的函数。它接受state和action作为参数,并返回下一个状态。
  • initialState:包含初始值,可以是任何类型。

深入了解useReducer

现在我们已经了解了构成useReducer钩子的部分,是时候更深入地了解它是如何运作的了。

要在React应用中使用useReducer,请在组件的顶层调用它。

javascript 复制代码
import { useReducer } from "react";

然后我们可以在组件中使用useReducer钩子:

javascript 复制代码
export default function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return(
    // JSX内容
  )
}

为了看到我们的useReducer钩子的实际效果,我们将构建一个非常简单的计数器应用程序,当点击增加按钮时计数加1,当点击减少按钮时计数减1。

首先,让我们仔细看看重要的reducer函数。这个函数决定了状态如何更新,并包含了计算下一个状态的所有逻辑。

基本上,reducer包含了在使用useState时通常放在事件处理程序中的逻辑。这使得在不获得期望结果时更容易阅读和调试。快速查看reducer函数可以为你节省很多麻烦。

reducer函数总是在组件外部声明,并接受当前状态和action作为参数。

javascript 复制代码
function reducer(state, action) {
  // 逻辑处理
}

action是一个通常具有type属性的对象,用于标识特定的操作。action描述了发生的事情,并包含reducer更新状态所需的信息。

我们使用条件语句来检查action类型并执行指定的操作,返回一个新的状态值。reducer中可以使用if和switch等条件语句。

Dispatch函数

这是由useReducer钩子返回的函数,负责将状态更新为新值。dispatch函数只接受action作为其唯一参数。

我们可以将dispatch函数放在事件处理函数内部。记住,action带有一个type属性,所以我们在调用dispatch函数时必须指定它。对于我们的计数器应用,我们有两个事件处理函数,用于增加和减少计数。

javascript 复制代码
function handleIncrement() {
  dispatch({ type: "increment" });
}

function handleDecrement() {
  dispatch({ type: "decrement" });
}

现在,我们回到我们的reducer函数,使用switch条件来评估action.type表达式。

javascript 复制代码
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + 1 };
    case "decrement":
      return { ...state, count: state.count - 1 };
    default:
      return "Unrecognized command";
  }
}

在上面的代码中:

  • reducer函数接受state和action作为参数。
  • 我们有条件地检查action.type表达式字符串的特定情况。
  • 如果为真,通过使用展开运算符获取state的浅拷贝,并评估state中的count值。
  • 评估完成后返回新的状态。
  • default作为在没有匹配情况时的后备选项。

我们计数器应用程序的整个逻辑已经完成。现在我们可以返回JSX,显示count状态在用户界面上,并将处理函数传递给按钮的onClick事件处理程序。

javascript 复制代码
return (
  <>
    <h1>计数:{state.count}</h1>
    <button onClick={handleIncrement}>增加</button>
    <button onClick={handleDecrement}>减少</button>
  </>
);

我们的计数器应用设置完毕,当按钮被点击时,计数状态将相应地更新。

幕后发生了什么?

点击按钮的动作触发一个dispatch函数,该函数向reducer函数发送类型信息。dispatch(点击按钮)导致组件重新渲染。reducer函数有条件地将case与来自action对象的type匹配,并在评估发生后相应地更新状态。

注意:在dispatch时,reducer函数仍然保持旧值。这意味着dispatch函数只更新下一次渲染的状态变量。为了检查这一点,我们可以在switch语句之前将state和action参数记录到控制台:

javascript 复制代码
function reducer(state, action) {
  console.log(state, action);

  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + 1 };
    case "decrement":
      return { ...state, count: state.count - 1 };
    default:
      return "Unrecognized command";
  }
}

在点击增加按钮两次后,控制台会显示:

上图显示,在第一次点击时,有一个类型为increment的action,初始状态值为0。在第二次点击时,状态值更新为1,并显示为当前计数状态。希望您现在能够理解这个过程。

让我们看一个reducer函数的真实世界例子。

Reducer的现实世界例子

想象一个在线购物公司的调度员去仓库获取他们稍后将分发给订购者的商品。

调度员表明自己的身份,并执行一个向仓库经理索要商品的动作。经理去装有已发货订单的箱子,找到要交给调度员的商品。经理还登录库存系统,进行评估,然后才将商品交给调度员。

这个场景也可以理解为:

  • 调度员请求更新或触发一个过程,就像dispatch函数一样。
  • 调度员执行"索要商品"的动作,就像带有type属性的dispatch action。
  • 仓库经理进行必要的分类和更新,就像reducer函数一样。
  • 装有所有商品的箱子根据有多少商品被清空以进行调度而更新。这就像状态更新。

希望这个真实世界的场景能让整个过程对你来说更加清晰。

再看一遍完整的代码,理解整个过程:

javascript 复制代码
import { useReducer } from "react";

function reducer(state, action) {
  console.log(state, action);
  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + 1 };
    case "decrement":
      return { ...state, count: state.count - 1 };
    default:
      return "Unrecognized command";
  }
}
const initialState = { count: 0 };
export default function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  function handleIncrement() {
    dispatch({ type: "increment" });
  }
  function handleDecrement() {
    dispatch({ type: "decrement" });
  }
  return (
    <>
      <h1>计数:{state.count}</h1>
      <button onClick={handleIncrement}>增加</button>
      <button onClick={handleDecrement}>减少</button>
    </>
  );
}

使用useReducer钩子的好处

  • 帮助集中状态逻辑。
  • 使状态转换可预测。
  • 适合复杂的状态管理。
  • 优化性能。

结论

我们已经介绍了什么是useReducer钩子,它与useState的比较------相似点和不同点,reducer过程以及使用useReducer的好处。

如果你觉得这篇文章有帮助,可以给我点个赞!


本文译自Timothy Olanrewaju的《How to Use the useReducer Hook in React》,由掘金用户翻译整理。

相关推荐
疏狂难除3 分钟前
基于SeaORM+MySQL+Tauri2+Vite+React等的CRUD交互项目
前端·react.js·前端框架
在广东捡破烂的吴彦祖2 小时前
React关闭缓存标签页刷新页面的hooks
react.js
FE_C_P小麦2 小时前
使用 Redux Toolkit封装一个模块化的 store
react.js·typescript·redux
随笔记2 小时前
用create-react-app脚手架创建react项目报错怎么办
前端·react.js·typescript
前端加油站5 小时前
Errorboundary详解
前端·react.js
小成C5 小时前
一文搞懂 React useState的内部机制:闭包状态持久化的奥秘
前端·javascript·react.js
IT、木易6 小时前
大白话解释 React 中高阶组件(HOC)的概念和应用场景,并实现一个简单的 HOC。
前端·javascript·react.js
束尘7 小时前
React前端开发中实现断点续传
前端·javascript·react.js
一蓑烟雨,一任平生7 小时前
React-state响应式内部数据(类组件&Hook两种方式整理)
前端·javascript·react.js