大厂进阶七: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 /…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...