大厂进阶七: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 来描述状态的变化意图,实现了可预测的状态管理和组件之间的数据共享。
一、useReducer与useState区别
由于useReducer是用在单一组件中多个状态管理,是否可以用useState代替呢?
useReducer和useState在某些情况下可以相互替代,但它们也有一些不同之处:
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在简单的状态更新场景下性能表现也很好。但如果在状态更新过程中存在复杂的计算或依赖关系,可能需要手动处理一些性能优化问题,比如使用useMemo或useCallback来避免不必要的重新渲染。
综上所述,虽然在单一组件中,对于简单的状态管理,useState可能更方便快捷,但对于复杂的状态逻辑和需要更好的性能优化的场景,useReducer是一个很好的选择。在实际开发中,可以根据具体的需求和项目特点来决定使用哪种方式。
二、 useReducer + useContext
以下是一个使用 useReducer 和 useContext 结合 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")
);
在这个示例中,通过定义明确的接口和类型,使用 useReducer 和 useContext 来管理状态和提供状态给组件,模拟了类似 Redux 的状态管理模式。
三、使用 useReducer 和 useContext 实现的购物车
以下是一个使用 useReducer 和 useContext 实现的购物车案例:
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状态管理全解析
前言: React 中用于状态管理的hook及库有:useState、useReducer、useContext、useReducer useContext和一些第三方的库如redux、mobx等。 1、useState 单一组件某个具体状态 2、useReducer 单一组件中多个状态管理,策略分发机制统一管理…...
【ocr识别003】flask+paddleocr+bootstrap搭建OCR文本推理WEB服务
1.欢迎点赞、关注、批评、指正,互三走起来,小手动起来! 2.了解、学习OCR相关技术知识领域,结合日常的场景进行测试、总结。如本文总结的flaskpaddleocrbootstrap搭建OCR文本推理WEB服务应用示例场景。 文章目录 1.代码结构2.效果演…...
从零开始搭建 LVS 高性能集群 (DR模式)
从零开始搭建 LVS 高性能集群 (DR模式) 架构 本设计方案采用三台服务器构建集群,使用Linux Virtual Server (LVS) 作为负载均衡器,运行在直接路由 (DR) 模式下。集群中的每一台服务器都将运行相同的服务,以实现 高可用…...
Linux环境开发工具【yum与vim】
🌈个人主页:Yui_ 🌈Linux专栏:Linux 🌈C语言笔记专栏:C语言笔记 🌈数据结构专栏:数据结构 文章目录 1.Linux软件包管理器yum1.1 快速使用yum 2. Linux编辑器-vim的使用2.1 vim的基本…...
laravel GuzzleHttp Client 无法获取返回的错误信息
Client发送一些请求,当返回状态不是200的时候,无法获取完整错误信息 $client new Client([base_uri > $this->getUri()./order/aaaaaa,timeout > 30,verify > false]);try {$response $client->request(POST, , [headers > [Lang&g…...
XMOS 多路音频解码器
当谈及高性能音频解码器,XMOS 是一个不容忽视的名字。作为音频解决方案领域的领军者,XMOS 的多路音频解码器在音频处理技术中扮演着至关重要的角色。下面我们一起深入探讨 XMOS 多路音频解码器去了解这一技术的魅力。 设计背景:追求音频极致…...
XSS小游戏(题目+解析)
xss题目练习地址: xss小游戏 游戏界面 一、Ma Spaghet! 我将题目要求进行翻译: 题目的主要要求就是:弹出一个(1337)的弹窗 开始解题: Let’s Go! 首先,传个参数看看 发现参数直接显示在了 < h2 >…...
《Redis核心技术与实战》学习笔记4——AOF日志:宕机了,Redis如何避免数据丢失?
文章目录 AOF 日志是如何实现的?三种写回策略 日志文件太大了怎么办?AOF 重写会阻塞吗?小结 大家好,我是大白。 如果有人问你:“你会把 Redis 用在什么业务场景下?”我想你大概率会说:“我会把它当作缓存使…...
NextJs - 服务端/客户端组件之架构多样性设计
NextJs - 服务端/客户端组件之架构多样性设计 前言一. 架构设计1.1 SSR流式渲染常见错误设计之 - 根页面同步阻塞1.2 架构设计之 - 客户端组件依赖于服务端组件数据① 使用 Redux 完成数据共享 1.3 架构设计之 - 单页内的分步骤跳转① 如何做到服务端组件和客户端组件之间的切换…...
使用 Python 进行 PDF 文件加密
使用 Python 解密加密的 PDF 文件-CSDN博客定义一个名为的函数,该函数接受三个参数:输入的加密 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屏幕制造工艺流程是一个复杂且精细的过程,涉及多个关键步骤以确保最终的显示效果和性能。以下是OLED屏幕制造工艺流程的主要步骤: 1. 衬底制作与准备 材料选择:OLED器件需要一个透明的导电衬底,通常使用玻璃或塑料材料。 清…...
knowLedge-VueCLI项目中环境变量的定义与使用
1. env 1.1简介 在 Vue CLI 创建的项目中,你可以通过 .env 文件来定义环境变量。Vue CLI 支持多种 .env 文件,它们根据文件名中的前缀来决定何时加载和使用这些环境变量。 以下是一些常见的 .env 文件及其用途: .env:在任何环境…...
【C#】 接口 继承
简介 继承是面向对象编程的核心特性之一,它允许我们创建一个类(称为子类)来继承另一个类(称为基类)的属性和方法。 作用 这样,我们可以重用代码,减少重复,并使我们的代码更加模块…...
Self-Supervised Learning(李宏毅老师系列)
自学参考: BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding BERT 论文逐段精读 视频课 课件资料 笔记 一、概述 自监督学习模型与芝麻街~ 参数量 ELMO:94MBERT:340MGPT-2:1542MMegatron&…...
8月16日笔记
只有DNS协议出网场景 DNS 协议是一种请求、应答协议,也是一种可用于应用层的隧道技术。DNS 隧道的工作原理很简单,在进行 DNS 查询时,如果查询的域名不在 DNS 服务器本机缓存中,就会访问互联网进行查询,然后返回结果。…...
苹果Mac电脑——装macOS和Windows双系统的方法
一、实验环境 在Windows系统电脑上制作MacOS启动U盘。准备一个大于16G的U盘。 二、实验步骤 2.1 在Windows系统电脑上制作MacOS启动U盘 MacOS镜像下载 在Windows系统电脑上制作MacOS启动U盘的方法 2.2 U盘插上苹果电脑,安装macOS系统 U盘插上苹果电脑…...
【C++ 面试 - 基础题】每日 3 题(十五)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏&…...
数学建模学习笔记
数学建模学习笔记 现学现卖,随缘更新QwQ 主要根据b站大师兄的视频整理而成,有不懂的可以去看原视频 List 数学建模学习笔记一、 层次分析法1.1 矩阵的一致性及其检验1.2 权重计算1.3 具体流程 二、模糊综合评测2.1 隶属函数2.2 隶属函数的确定方法2.3 模…...
个人可识别信息(PII) AI 去除 API 数据接口
个人可识别信息(PII) AI 去除 API 数据接口 ai / 隐私保护 基于 AI 模型自动去除个人识别信息(PII) 个人信息保护 / AI 模型 。 1. 产品功能 基于自有专业模型进行 PII 自动去除高效处理敏感信息全接口支持 HTTPS(TLS v1.0 / v1.1 / v1.2 /…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 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(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
