第32节——useReducer——了解

news2025/5/31 18:24:05

一、概念

useReducer 是在 react V 16.8 推出的钩子函数,从用法层面来说是可以代替useState。众所周知,useState 常用在单个组件中进行状态管理,但是遇到状态全局管理的时候,useState 显然不能满足我们的需求,这个时候大多数的做法是利用第三方的状态管理工具,像 redux,Recoil 或者 Mobx。

import XXX from Mobx; 
import XXX from Redux; // or import XXX from Recoil;

强大的 React 团队难道就不能自己实现一个全局的状态管理的 hook 吗,这不,useReducer 为了解决这个需求应运而生。

二、基本用法

useReducer 钩子用来存储和更新状态,有点类似 useState 钩子。在用法上,它接收一个reducer函数作为第一个参数,第二个参数是初始化的state。useReducer最终返回一个存储有当前状态值和dispatch函数的数组,该dispatch函数触发的时,会调用reducer的方法,reducer方法返回的值会更新state

const [count, dispatch] = useReducer(reducer, initialState);

三、使用场景

1、当多个 state 需要一起更新时

2、当state 更新逻辑较复杂

3、当下一个 state 依赖于之前的 state,即 编写 setState(prevState => newState)时

包括但不限于以上三种

四、在以上场景中使用时,useReducer()相对于 useState() 的优势

1、useReducer 相对于 useState 可以更好的描述“如何更新状态”。 比如:useReducer 能够读取相关的状态、同时更新多个状态。

2、组件负责发出 action,reducer 负责更新状态的模式, 使得代码逻辑更加清晰。代码行为更加可以预测(比useEffect 的更新时机更加稳定)

3、通过传递 dispatch ,可以减少状态值的传递。 useReducer 总是返回相同的 dispatch 函数,这是彻底解耦的标志:状态更新逻辑可以任意变化,而发起 action 的渠道始终不变。

五、两者对比的例子

每秒对输入框里输入的数值进行累加的操作

img

1、useState的写法

import { useEffect, useState } from "react";

export default () => {
  const [num, setNum] = useState(0);
  const [inputVal, setInputVal] = useState(1);

  useEffect(() => {
    const ter = setInterval(() => {
      console.log("一直不变的num", num)
      // 当前数据的更新依赖上一次state的状态
      setNum((prevNum) => {
        console.log("最新的值num", prevNum)
        return prevNum + inputVal
      });
    }, 1000);

    return () => {
      clearInterval(ter);
    };
    /**
     * 还要考虑什么时候更新闭包里面的数据
     *
     * 为什么已经有更新闭包数据了,还需要拿num上一次的数据呢?
     * 我们更新的数据是基于每次inputVal状态变的时候更新,所以我们能拿到
     * 最新的inputVal,但是num可能拿不到最新的
     */
  }, [inputVal]);

  return (
    <div>
      {num}
      <div>
        <input
          type="number"
          value={inputVal}
          onChange={(e) => setInputVal(Number(e.target.value))}
        />
      </div>
    </div>
  );
};

2、useReducer

useReducer()方法使得组件只需要发出action,而无需知道如何更新状态。另外,此时 step 的更新不会造成 useEffect 的失效、重执行。

import { useEffect, useReducer } from "react";

export default () => {
  const reducer = (state, action) => {
    switch (action.type) {
      case "addNum":
        return { ...state, num: state.num + state.inputVal };
      case "inputChange":
        return { ...state, ...action.payload };
      default:
        console.warn("当前没有可执行逻辑,请检查代码")
        return state
    }
  };

  const [state, dispatch] = useReducer(reducer, {
    num: 0,
    inputVal: 1,
  });

  useEffect(() => {
    setInterval(() => {
      dispatch({
        type: "addNum",
      });
    }, 1000);
  }, []);
  console.log(state)
  return (
    <div>
      {state.num}
      <div>
        <input
          type="number"
          value={state.inputVal}
          onChange={(e) =>
            dispatch({
              type: "inputChange",
              payload: {
                inputVal: Number(e.target.value),
              },
            })
          }
        />
      </div>
    </div>
  );
};

六、useReducer + useContext 实现状态共享

试想一下,如果想实现以下组件需求:

1、父组件中定义某变量xx;

2、任何层级下的子组件都可以轻松获取变量xx、并且可以“修改”变量xx;

3、同级之间可以任意传值

注意

这里的修改是加引号的,因为事实上你永远无法以直接赋值的方式进行修改,永远都需要调用父级组件提供的方法来修改。

实现思路

用 useContext 实现“获取全局数据”

用 useReducer 实现“修改全局数据”

1、父级组件

import { useReducer } from "react";
import AComponent from "./a";
import BComponent from "./b";
import MyContext from "./context";

/**
 *
 * 儿子可以直接拿到爷爷的状态以及新修改他的状态
 */
export default function LearnUseReducer4() {
  const reducer = (state, action) => {
    switch (action.type) {
      case "add":
        return { ...state, num: state.num + 1 };
      case "childAddNum":
        return { ...state, childNum: state.childNum + 1 };
    }
  };

  const [state, dispatch] = useReducer(reducer, {
    num: 1,
    childNum: 1,
  });

  return (
    <div>
      <div>爷爷</div>
      <MyContext.Provider
        value={{
          state,
          dispatch,
        }}
      >
        <AComponent></AComponent>
        <BComponent></BComponent>
      </MyContext.Provider>
    </div>
  );
}

2、a组件

import { useContext, useState } from "react";
import CComponent from "./c";
import MyContext from "./context";

export default function AComponent() {
  const context = useContext(MyContext);

  /**
   * 希望把这个num属性传递给叔叔这个组件
   * 那么首页要把num属性进行状态提升
   */
  // const [num, setNum] = useState(1)

  return (
    <div>
      <div>爸爸 -- {context.state.childNum}</div>
      {/* <button onClick={() => setNum(num + 1)}>num + 1</button> */}
      <button
        onClick={() =>
          context.dispatch({
            type: "childAddNum",
          })
        }
      >
        num + 1
      </button>
      <CComponent></CComponent>
    </div>
  );
}

3、b组件

import MyContext from "./context";
import { useContext } from "react";
export default function BComponent() {
  const context = useContext(MyContext);

  return (
    <div>
      <div>叔叔 --- {context.state.childNum}</div>
    </div>
  );
}

4、c组件

import { useContext } from "react";
import MyContext from "./context";

export default function CComponent() {
  const context = useContext(MyContext);

  return (
    <div>
      <div>儿子 --- {context.state.num}</div>
      <button
        onClick={() =>
          context.dispatch({
            type: "add",
          })
        }
      >
        修改爷爷的方法
      </button>
    </div>
  );
}
ext = useContext(MyContext);

  return (
    <div>
      <div>儿子 --- {context.state.num}</div>
      <button
        onClick={() =>
          context.dispatch({
            type: "add",
          })
        }
      >
        修改爷爷的方法
      </button>
    </div>
  );
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1027402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4G工业路由器高效数据传输助力光伏发电站管理

光伏发电站是能源产业中一种利用太阳能技术将光转化为电能的常见设施。随着物联网技术与环保能源的不断进步和应用的普及&#xff0c;光伏发电站的管理也变得更加便捷高效。 光伏发电站结合4G工业路由器实现远程监控管理&#xff0c;并用于采集发电站中的传感器数据和监控信息…

vue watch 侦听器 监视器

vue watch 侦听器 监视器 变量 变化的时候&#xff0c;自动调用处理函数 vue watch 侦听器 监视器

/node_modules/XXX/index.js:XXX XXX ??= X;SyntaxError: Unexpected token ‘??=‘

这问题 老实说有点奇葩 不影响运行 反倒运行提交了 不解决这个问题提交不了代码 这个错误是由于语法不兼容导致的。?? 是一个相对较新的 JavaScript 语法&#xff0c;也就是空值合并赋值操作符&#xff0c;它在 Node.js 版本低于 15 或者某些浏览器中不被支持。 那么 了解…

日常生活中的常用命令及操作

目录 一、Windows11 中查看网卡名称 及ip地址 二、查看硬件的详细信息 三、查看显卡声卡详细信息及厂商 四、C盘清理 第一步 输入wini 开启Windows设置主界面 第二步 存储中还有一个叫存储感知的功能 第三步 更改新内容的保存位置 第四步 怕误C盘内的东西可以 查看详细的…

【Java 基础篇】Java 线程通信详解

多线程编程在实际应用中非常常见&#xff0c;但随之而来的问题是线程之间的通信。线程通信是多线程编程中一个至关重要的概念&#xff0c;它涉及到线程之间的信息传递、同步和协作。本篇博客将详细解释Java中的线程通信&#xff0c;包括什么是线程通信、为什么需要线程通信、如…

UG\NX二次开发 求空间点坐标按照某方向移动某距离后的新坐标

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 简介: 群友问“ufun中空间点的坐标(x,y,z),沿着空间任意工作坐标系z轴移动10mm后的新坐标如何计算?” 这个是有多种方法的,比如数学计算比如ufun的仿射变换…

css,环形

思路&#xff1a; 1.先利用conic-gradient属性画一个圆&#xff0c;然后再叠加 效果图 <template><div class"ring"><div class"content"><slot></slot></div></div> </template> <script> import …

JavaScript系列从入门到精通系列第三篇:JavaScript基本语法(一)

文章目录 一&#xff1a;JavaScript基本语法 1&#xff1a;JS注释 (一)&#xff1a;JS多行注释 (二)&#xff1a;JS单行注释 (三)&#xff1a;JS中大小写 (四)&#xff1a;分号问题 (五)&#xff1a;空格和换行 2&#xff1a;字面量和变量 (一)&#xff1a;字面量 (二…

demo1-csa(从初阶到大牛)

1.1文件管理命令练习 (1) 在/opt目录下创建一个临时目录tmp; # 创建临时目录tmp sudo mkdir /opt/tmp (2) 在临时目录下创建一个文件&#xff0c;文件名为a.txt; # 在临时目录下创建文件a.txt&#xff08;可以使用touch命令创建空文件&#xff09; sudo touch /opt/tmp/a.t…

MQ - 11 Kafka的架构设计与实现

文章目录 导图概述Kafka 系统架构协议和网络模块数据存储元数据存储消息数据生产者和消费者生产者消费者HTTP 协议支持和管控操作Kafka 从生产到消费的全过程总结导图 概述 在学习的过程中,我们会发现 Kafka 和 RocketMQ 的架构是非常像的,那为什么还要单独来分析 Kafka 呢?…

脑电相关临床试验及数据分析

临床试验设计 作为一个医疗器械公司的开发–>算法–>项目–>产品&#xff0c;还是想在这里记录一下工作。 直接开始吧 临床试验的设计&#xff0c;主要分为20个部分&#xff0c;分别是 封面 一、申办者信息 二、所有临床试验机构和研究者列表 三、临床试验的目的和…

计算机视觉与深度学习-全连接神经网络-详解梯度下降从BGD到ADAM - [北邮鲁鹏]

文章目录 参考文章及视频导言梯度下降的原理、过程一、什么是梯度下降&#xff1f;二、梯度下降的运行过程 批量梯度下降法(BGD)随机梯度下降法(SGD)小批量梯度下降法(MBGD)梯度算法的改进梯度下降算法存在的问题动量法(Momentum)目标改进思想为什么有效动量法还有什么效果&…

硬件学习 PAD9.5 day02 原理图绘制中的操作,PCB 封装的制作

1. 原理图中的连线操作 1.1 点击连线按钮 1.2 点击需要连线的地方连接即可 1.3 双击即可停止连线 2. 原理图的总线绘制 2.1 按下总线绘制按钮 2.2 画一条总线 总线名称 总线名字 [ 起始数字 &#xff1a; 结束数字 ] 2.3 分线连接总线 注意&#xff1a;原理图的连线…

【Java 基础篇】Java 多线程详解

多线程是 Java 编程中的一个重要概念&#xff0c;它允许程序同时执行多个任务&#xff0c;提高了程序的性能和响应能力。本篇博客将深入探讨 Java 多线程&#xff0c;从基础概念到实际应用&#xff0c;适用于 Java 初学者和希望深入了解多线程的开发人员。 什么是多线程&#…

【JS】—垃圾回收机制

一、指令材料 1.定义 JavaScript&#xff08;JS&#xff09;的垃圾回收机制是一种自动管理内存的过程&#xff0c;它有助于释放不再使用的内存&#xff0c;以避免内存泄漏和提高程序的性能。 JavaScript的垃圾回收机制是一种自动管理内存的方式&#xff0c;以确保不再被引用的…

【RocketMQ】路由中心NameServer

【RocketMQ】路由中心NameServer 参考资料&#xff1a; RocketMQ Nameserver 背后的设计理念 RocketMQ之NameServer详解 深入剖析RocketMQ源码-NameServer —— vivo互联网技术 《RocketMQ技术内幕》 文章目录 【RocketMQ】路由中心NameServerNameServer架构设计NameServer工作…

上海亚商投顾:沪指震荡调整 两市成交金额跌破6000亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日集体调整&#xff0c;创业板指续创3年多以来新低。ST板块继续走强&#xff0c;*ST柏龙、ST恒久等…

笔记2.2:网络应用基本原理

一. 网络应用的体系结构 &#xff08;1&#xff09;客户机/服务器结构&#xff08;Client-Server, C/S&#xff09; &#xff08;2&#xff09;点对点结构&#xff08;Peer-to-Peer&#xff0c;P2P&#xff09; &#xff08;3&#xff09;混合结构&#xff08;Hybrid&#x…

leetcode646. 最长数对链(java)

最长数对链 题目描述贪心解法二 动态规划 dp 题目描述 难度 - 中等 leetcode646. 最长数对链(java) 给你一个由 n 个数对组成的数对数组 pairs &#xff0c;其中 pairs[i] [lefti, righti] 且 lefti < righti 。 现在&#xff0c;我们定义一种 跟随 关系&#xff0c;当且仅…

数字散斑干涉测量仿真研究

一、引言 数字散斑干涉技术(digital speckle pattern interferometry&#xff0c;DSPI)是一种测量物体表面微小变形的测量技术&#xff0c;在生物医学检测、缺陷无损检测、精密制造、材料与结构力学参数评估等领域起着日益重要的作用&#xff0c;具有实时性、高精度、非接触、…