当前位置: 首页 > news >正文

大厂进阶七:React状态管理全解析

前言:

React 中用于状态管理的hook及库有:useState、useReducer、useContext、useReducer + useContext和一些第三方的库如redux、mobx等。

1、useState

单一组件某个具体状态

2、useReducer

单一组件中多个状态管理,策略分发机制统一管理

3、useContext

创建了一个全局状态机制,可分发给下面所有的children

4、useReducer + useContext

使用 React.createContext 创建一个 Context 对象。这个 Context 将用于在组件树中传递全局状态和更新状态的方法,Context 通常会有一个 Provider 组件,Provider 组件内部,使用 useReducer 来管理全局状态

5、redux库

Redux 的原理是通过单一的不可变的存储中心(store)来保存应用的状态,利用纯函数(reducer)来处理状态的更新,借助 action 来描述状态的变化意图,实现了可预测的状态管理和组件之间的数据共享。

一、useReduceruseState区别

由于useReducer是用在单一组件中多个状态管理,是否可以用useState代替呢?
useReduceruseState在某些情况下可以相互替代,但它们也有一些不同之处:

1、功能和使用场景

1.1 useReducer通常适用于以下场景:

复杂的状态逻辑管理

当状态的更新逻辑较为复杂,包含多个子状态之间的相互依赖关系或多个不同的操作类型时,useReducer可以使代码更清晰、更具可读性和可维护性。

共享状态逻辑

如果多个组件需要共享相同的状态更新逻辑,useReducer可以将 reducer 函数提取出来,方便在不同组件中复用。

优化性能

在某些情况下,useReducer会比useState更高效,特别是当状态更新频繁且计算量较大时,因为 reducer 函数通常是纯函数,React 可以更有效地进行优化。

1.2 useState则更适合以下场景:

简单的状态管理

当状态更新逻辑比较简单,只有一个或几个基本的状态值需要管理时,useState是一个更直接和简洁的选择。

小型组件

对于小型组件或不需要复杂状态管理的组件,useState的语法更简单,代码量更少。

2、代码结构和可读性

2.1 useReducer

使用useReducer时,需要定义一个 reducer 函数来处理状态的更新。这个 reducer 函数接收当前的状态和一个动作(action)作为参数,并返回新的状态。

import { useReducer } from 'react';const initialState = { count: 0 };function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}
}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<div><p>Count: {state.count}</p><button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
}

这种方式将状态更新的逻辑集中在 reducer 函数中,使得代码结构更清晰,尤其是当状态更新逻辑复杂时,更容易理解和维护。

2.2 useState

useState的语法相对简单,直接返回一个状态值和一个更新状态的函数。

import { useState } from 'react';function Counter() {const [count, setCount] = useState(0);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>+</button><button onClick={() => setCount(count - 1)}>-</button></div>);
}
对于简单的状态更新,`useState`的代码更简洁直观,易于理解。

3、性能优化方面

3.1 useReducer

在某些情况下,useReducer可以更好地进行性能优化。因为 reducer 函数是纯函数,React 可以更容易地跟踪状态的变化,并根据需要进行优化。例如,当状态更新是基于前一个状态值计算时,useReducer可以确保每次更新都是基于最新的状态值,避免潜在的竞态条件。
如果状态更新的计算量较大,将计算逻辑放在 reducer 函数中,可以让 React 更好地理解状态的变化过程,从而进行更有效的优化。

3.2 useState

useState在简单的状态更新场景下性能表现也很好。但如果在状态更新过程中存在复杂的计算或依赖关系,可能需要手动处理一些性能优化问题,比如使用useMemouseCallback来避免不必要的重新渲染。

综上所述,虽然在单一组件中,对于简单的状态管理,useState可能更方便快捷,但对于复杂的状态逻辑和需要更好的性能优化的场景,useReducer是一个很好的选择。在实际开发中,可以根据具体的需求和项目特点来决定使用哪种方式。

二、 useReducer + useContext

以下是一个使用 useReduceruseContext 结合 TypeScript 来模拟类似 Redux 功能的代码示例:

1. 创建 types.ts 文件来定义接口和类型:

// 定义动作类型
export enum ActionTypes {INCREMENT = "INCREMENT",DECREMENT = "DECREMENT",
}// 定义状态接口
export interface IState {count: number;
}// 定义动作接口
export interface IAction {type: ActionTypes;
}

2. 创建 reducer.ts 文件来定义 reducer 函数:

import { IState, IAction, ActionTypes } from "./types";const initialState: IState = {count: 0,
};const reducer = (state: IState = initialState, action: IAction): IState => {switch (action.type) {case ActionTypes.INCREMENT:return { count: state.count + 1 };case ActionTypes.DECREMENT:return { count: state.count - 1 };default:return state;}
};export {reducer, initialState};

3. 创建 context.ts 文件来定义 Context 和 Provider:

import { createContext, useReducer, Dispatch } from "react";
import { IAction} from "./types";import {reducer, initialState}from "./reducer";// 创建 Context
export const StoreContext = createContext<{state: IState;dispatch: Dispatch<IAction>;
} | null>(null);// 创建 Provider 组件
export const StoreProvider: React.FC = ({ children }) => {const [state, dispatch] = useReducer(reducer, initialState);return (<StoreContext.Provider value={{ state, dispatch }}>{children}</StoreContext.Provider>);
};

4. 在组件中使用:

import React from "react";
import { StoreContext } from "./context";
import { useContext } from "react";const MyComponent: React.FC = () => {const { state, dispatch } = useContext(StoreContext)!;const increment = () => {dispatch({ type: ActionTypes.INCREMENT });};const decrement = () => {dispatch({ type: ActionTypes.DECREMENT });};return (<div><p>Count: {state.count}</p><button onClick={increment}>+</button><button onClick={decrement}>-</button></div>);
};export default MyComponent;

5. 在应用的入口文件中包裹:

import React from "react";
import ReactDOM from "react-dom";
import { StoreProvider } from "./context";
import MyComponent from "./MyComponent";ReactDOM.render(<StoreProvider><MyComponent /></StoreProvider>,document.getElementById("root")
);

在这个示例中,通过定义明确的接口和类型,使用 useReduceruseContext 来管理状态和提供状态给组件,模拟了类似 Redux 的状态管理模式。

三、使用 useReduceruseContext 实现的购物车

以下是一个使用 useReduceruseContext 实现的购物车案例:

1. 首先创建一个 Context

import React from 'react';// 创建购物车上下文
const CartContext = React.createContext();

2. 定义 reducer 函数和初始状态:

// 初始购物车状态
const initialCartState = {items: [],
};// 购物车 reducer 函数
function cartReducer(state, action) {switch (action.type) {case 'ADD_ITEM':// 检查是否已存在该商品,若存在则增加数量,否则添加新商品const existingItemIndex = state.items.findIndex((item) => item.id === action.payload.id);if (existingItemIndex!== -1) {const updatedItems = [...state.items];updatedItems[existingItemIndex].quantity += 1;return {...state, items: updatedItems };} else {return {...state,items: [...state.items,{...action.payload, quantity: 1 },],};}case 'REMOVE_ITEM':return {...state,items: state.items.filter((item) => item.id!== action.payload),};default:return state;}
}

3. 创建 Provider 组件:

function CartProvider({ children }) {const [cartState, dispatch] = React.useReducer(cartReducer, initialCartState);const addItemToCart = (item) => {dispatch({ type: 'ADD_ITEM', payload: item });};const removeItemFromCart = (itemId) => {dispatch({ type: 'REMOVE_ITEM', payload: itemId });};const value = {cartState,addItemToCart,removeItemFromCart,};return (<CartContext.Provider value={value}>{children}</CartContext.Provider>);
}

4. 在组件中使用:

function Product({ product, addToCart }) {return (<div><h3>{product.name}</h3><p>{product.price}</p><button onClick={() => addToCart(product)}>添加到购物车</button></div>);
}function Cart() {const { cartState, removeItemFromCart } = React.useContext(CartContext);return (<div><h2>购物车</h2>{cartState.items.map((item) => (<div key={item.id}><p>{item.name} - 数量: {item.quantity}</p><button onClick={() => removeItemFromCart(item.id)}>从购物车中移除</button></div>))}</div>);
}function App() {return (<CartProvider><Productproduct={{ id: 1, name: '商品 1', price: 10 }}addToCart={(product) => addItemToCart(product)}/><Cart /></CartProvider>);
}export default App;

在这个例子中:

  • CartContext 用于创建购物车的上下文。
  • cartReducer 处理购物车状态的更新逻辑,包括添加商品和移除商品。
  • CartProvider 组件使用 useReducer 创建状态和 dispatch 函数,并将其和操作函数通过 CartContext.Provider 提供给子组件。
  • Product 组件表示一个商品,点击按钮可以将商品添加到购物车。
  • Cart 组件展示购物车中的商品,并提供移除商品的功能。

四、Redux

React 使用 Redux 进行状态管理的代码实例:

1. 首先安装必要的库:

npm install redux react-redux

2. 创建 Redux 的 store

创建一个 store.js 文件:

import { createStore } from 'redux';// 定义初始状态
const initialState = {count: 0
};// 定义 reducer
const counterReducer = (state = initialState, action) => {switch (action.type) {case 'INCREMENT':return { count: state.count + 1 };case 'DECREMENT':return { count: state.count - 1 };default:return state;}
};// 创建 store
const store = createStore(counterReducer);export default store;

3. 创建 React 组件:

App.js

import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';const App = ({ count, increment, decrement }) => {return (<div><p>Count: {count}</p><button onClick={increment}>+</button><button onClick={decrement}>-</button></div>);
};// 从 Redux store 中获取 state 映射到组件的 props
const mapStateToProps = (state) => ({count: state.count
});// 将 action creators 映射到组件的 props
const mapDispatchToProps = {increment,decrement
};// 使用 connect 连接组件和 Redux store
export default connect(mapStateToProps, mapDispatchToProps)(App);

4. 创建 actions.js 文件来定义 action creators

export const increment = () => ({type: 'INCREMENT'
});export const decrement = () => ({type: 'DECREMENT'
});

5. 在 index.js 中渲染应用并连接 store

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import store from './store';ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root')
);

在这个例子中,通过 Redux 管理了一个简单的计数器状态,reducer 根据不同的 action 类型来更新状态,React 组件通过 connect 函数从 Redux store 中获取状态和 action creators 来触发状态的更新。

相关文章:

大厂进阶七:React状态管理全解析

前言&#xff1a; React 中用于状态管理的hook及库有&#xff1a;useState、useReducer、useContext、useReducer useContext和一些第三方的库如redux、mobx等。 1、useState 单一组件某个具体状态 2、useReducer 单一组件中多个状态管理&#xff0c;策略分发机制统一管理…...

【ocr识别003】flask+paddleocr+bootstrap搭建OCR文本推理WEB服务

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 2.了解、学习OCR相关技术知识领域&#xff0c;结合日常的场景进行测试、总结。如本文总结的flaskpaddleocrbootstrap搭建OCR文本推理WEB服务应用示例场景。 文章目录 1.代码结构2.效果演…...

从零开始搭建 LVS 高性能集群 (DR模式)

从零开始搭建 LVS 高性能集群 &#xff08;DR模式&#xff09; 架构 本设计方案采用三台服务器构建集群&#xff0c;使用Linux Virtual Server (LVS) 作为负载均衡器&#xff0c;运行在直接路由 (DR) 模式下。集群中的每一台服务器都将运行相同的服务&#xff0c;以实现 高可用…...

Linux环境开发工具【yum与vim】

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 文章目录 1.Linux软件包管理器yum1.1 快速使用yum 2. Linux编辑器-vim的使用2.1 vim的基本…...

laravel GuzzleHttp Client 无法获取返回的错误信息

Client发送一些请求&#xff0c;当返回状态不是200的时候&#xff0c;无法获取完整错误信息 $client new Client([base_uri > $this->getUri()./order/aaaaaa,timeout > 30,verify > false]);try {$response $client->request(POST, , [headers > [Lang&g…...

XMOS 多路音频解码器

当谈及高性能音频解码器&#xff0c;XMOS 是一个不容忽视的名字。作为音频解决方案领域的领军者&#xff0c;XMOS 的多路音频解码器在音频处理技术中扮演着至关重要的角色。下面我们一起深入探讨 XMOS 多路音频解码器去了解这一技术的魅力。 设计背景&#xff1a;追求音频极致…...

XSS小游戏(题目+解析)

xss题目练习地址&#xff1a; xss小游戏 游戏界面 一、Ma Spaghet! 我将题目要求进行翻译: 题目的主要要求就是&#xff1a;弹出一个&#xff08;1337&#xff09;的弹窗 开始解题&#xff1a; Let’s Go! 首先&#xff0c;传个参数看看 发现参数直接显示在了 < h2 >…...

《Redis核心技术与实战》学习笔记4——AOF日志:宕机了,Redis如何避免数据丢失?

文章目录 AOF 日志是如何实现的&#xff1f;三种写回策略 日志文件太大了怎么办&#xff1f;AOF 重写会阻塞吗?小结 大家好&#xff0c;我是大白。 如果有人问你&#xff1a;“你会把 Redis 用在什么业务场景下&#xff1f;”我想你大概率会说&#xff1a;“我会把它当作缓存使…...

NextJs - 服务端/客户端组件之架构多样性设计

NextJs - 服务端/客户端组件之架构多样性设计 前言一. 架构设计1.1 SSR流式渲染常见错误设计之 - 根页面同步阻塞1.2 架构设计之 - 客户端组件依赖于服务端组件数据① 使用 Redux 完成数据共享 1.3 架构设计之 - 单页内的分步骤跳转① 如何做到服务端组件和客户端组件之间的切换…...

使用 Python 进行 PDF 文件加密

使用 Python 解密加密的 PDF 文件-CSDN博客定义一个名为的函数&#xff0c;该函数接受三个参数&#xff1a;输入的加密 PDF 文件路径input_pdf、输出的解密 PDF 文件路径output_pdf和密码password。https://blog.csdn.net/qq_45519030/article/details/141256661 在数字化时代…...

Spring Boot集成RabbitMQ

目录 1.RabbitMQ简介2.添加依赖3.配置RabbitMQ连接4.DirectExchange4.1 消费者4.2 生产者4.3 测试4.4 一个交换机对多个队列4.5 一个队列对多个消费者 5.FanoutExchange5.1 消费者5.2 生产者5.3 测试 6.TopicExchange6.1 消费者6.2 生产者 1.RabbitMQ简介 RabbitMQ是一个由Erl…...

OLED屏幕制造工艺流程

OLED屏幕制造工艺流程是一个复杂且精细的过程&#xff0c;涉及多个关键步骤以确保最终的显示效果和性能。以下是OLED屏幕制造工艺流程的主要步骤&#xff1a; 1. 衬底制作与准备 材料选择&#xff1a;OLED器件需要一个透明的导电衬底&#xff0c;通常使用玻璃或塑料材料。 清…...

knowLedge-VueCLI项目中环境变量的定义与使用

1. env 1.1简介 在 Vue CLI 创建的项目中&#xff0c;你可以通过 .env 文件来定义环境变量。Vue CLI 支持多种 .env 文件&#xff0c;它们根据文件名中的前缀来决定何时加载和使用这些环境变量。 以下是一些常见的 .env 文件及其用途&#xff1a; .env&#xff1a;在任何环境…...

【C#】 接口 继承

简介 继承是面向对象编程的核心特性之一&#xff0c;它允许我们创建一个类&#xff08;称为子类&#xff09;来继承另一个类&#xff08;称为基类&#xff09;的属性和方法。 作用 这样&#xff0c;我们可以重用代码&#xff0c;减少重复&#xff0c;并使我们的代码更加模块…...

Self-Supervised Learning(李宏毅老师系列)

自学参考&#xff1a; BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding BERT 论文逐段精读 视频课 课件资料 笔记 一、概述 自监督学习模型与芝麻街~ 参数量 ELMO&#xff1a;94MBERT&#xff1a;340MGPT-2&#xff1a;1542MMegatron&…...

8月16日笔记

只有DNS协议出网场景 DNS 协议是一种请求、应答协议&#xff0c;也是一种可用于应用层的隧道技术。DNS 隧道的工作原理很简单&#xff0c;在进行 DNS 查询时&#xff0c;如果查询的域名不在 DNS 服务器本机缓存中&#xff0c;就会访问互联网进行查询&#xff0c;然后返回结果。…...

苹果Mac电脑——装macOS和Windows双系统的方法

一、实验环境 在Windows系统电脑上制作MacOS启动U盘。准备一个大于16G的U盘。 二、实验步骤 2.1 在Windows系统电脑上制作MacOS启动U盘 MacOS镜像下载 在Windows系统电脑上制作MacOS启动U盘的方法 2.2 U盘插上苹果电脑&#xff0c;安装macOS系统 U盘插上苹果电脑&#xf…...

【C++ 面试 - 基础题】每日 3 题(十五)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…...

数学建模学习笔记

数学建模学习笔记 现学现卖&#xff0c;随缘更新QwQ 主要根据b站大师兄的视频整理而成&#xff0c;有不懂的可以去看原视频 List 数学建模学习笔记一、 层次分析法1.1 矩阵的一致性及其检验1.2 权重计算1.3 具体流程 二、模糊综合评测2.1 隶属函数2.2 隶属函数的确定方法2.3 模…...

个人可识别信息(PII) AI 去除 API 数据接口

个人可识别信息(PII) AI 去除 API 数据接口 ai / 隐私保护 基于 AI 模型自动去除个人识别信息&#xff08;PII&#xff09; 个人信息保护 / AI 模型 。 1. 产品功能 基于自有专业模型进行 PII 自动去除高效处理敏感信息全接口支持 HTTPS&#xff08;TLS v1.0 / v1.1 / v1.2 /…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...