大厂进阶七: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 /…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...

解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...