第32节——useReducer——了解

2023-09-21 08:00:00

一、概念

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>
  );
}
更多推荐

计算机竞赛 深度学习 机器视觉 人脸识别系统 - opencv python

文章目录0前言1机器学习-人脸识别过程人脸检测人脸对其人脸特征向量化人脸识别2深度学习-人脸识别过程人脸检测人脸识别MetricLarning3最后0前言🔥优质竞赛项目系列,今天要分享的是🚩深度学习机器视觉人脸识别系统该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!🥇学长这里给一个题目综合评分(每项满分5分)

计算机毕设 opencv python 深度学习垃圾图像分类系统

文章目录0前言课题简介一、识别效果二、实现1.数据集2.实现原理和方法3.网络结构最后0前言🔥这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业

Anaconda和Pycharm详细安装 配置教程

Anaconda:是一个开源的Python发行版本,其中包含了conda、Python等180多个科学包及其依赖项。【Anaconda下载】PyCharm:PyCharm是一种PythonIDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。【PyCharm下载】Anaconda的安装及环境配置一

JavaScript面试题整理(二)

数据类型篇13、其他值到字符串的转换规则?Null和Undefined类型,null转换为‘null’,undefined转换为‘undefined’Boolean类型,true转换为‘true’,false转换为‘false’Number类型的值直接转换,不过那些极小和极大的数字会使用指数形式Symbol类型的值直接

React(react18)中组件通信04——redux入门

React(react18)中组件通信04——redux入门1.前言1.1React中组件通信的其他方式1.2介绍redux1.2.1参考官网1.2.2redux原理图1.2.3redux基础介绍1.2.3.1action1.2.3.2store1.2.3.3reducer1.3安装redux2.redux入门例子3.

算法、数据结构、计算机系统、数据库MYSQL、概率论、数学实验MATLAB、数学建模、马原、英语、杂项、QT项目

算法冒号表达式(condition)?x:y可以三个条件以此类推(condition1)?x:(condition2)?y:z判断三角形最简单的办法boolcanFormTriangle(inta,intb,intc){return(a+b>c)&&(b+c>a)&&(a+c>b);}带空格的数据输入#include<

【ROS入门】创建工作空间与功能包

文章结构工作空间文件结构创建工作空间流程创建工作空间编译工作空间设置环境变量/创建功能包创建功能包编译功能包检查环境变量工作空间文件结构工作空间(workspace)是一个存放工程开发相关文件的文件夹,类似于在windows中使用IDE创建的工程。主要分为以下四个文件夹:src:代码空间(SourceSpace):用于

Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单

项目说明随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范,以及审计监督要求;通过电子化平台提高招投标工作的公开性和透明性;通过电子化招投标,使得招标采购的质量更高、速度

【2023,学点儿新Java-27】是的——C语言中的const关键字 | 附:按照类型 快速了解与划分:C语言中的关键字 | goto关键字解释

前情回顾:【2023,学点儿新Java-26】关键字介绍+示例代码:assert断言(如何启用断言),以验证一个数组的长度是否不为零为例说明【2023,学点儿新Java-25】如何解决浮点计算存在误差:strictfp|如何保护敏感信息,提高程序的可靠性和安全性:transient|附:Java异常处理关键字介绍【20

Spring Boot启动源码分析

一,前言版本:spring-boot-starter-parent版本为2.3.0SpringBoot项目的启动入口是一个main方法,因此我们从该方法入手即可二,源码分析跟踪run方法/***SpringApplication的方法*@paramprimarySource启动类的class*@paramargs启动参

GPT,GPT-2,GPT-3,InstructGPT的进化之路

ChatGPT火遍圈内外,突然之间,好多人开始想要了解NLP这个领域,想知道ChatGPT到底是个什么?作为在这个行业奋斗5年的从业者,真的很开心让人们知道有一群人在干着这么样的一件事情。这也是我结合各位大佬的文章,总结下GPT这条技术路线的初心。其实,ChatGPT的成功并非一朝一夕,而是OpenAI长达4年多持续努

热文推荐