Redux 与 React 状态管理精讲:从基础到实战

引言

Redux 是一个广泛使用的 JavaScript 状态管理库,尤其适用于 React 应用。它提供了一种可预测的方式来管理应用的状态,使得状态的变更变得可控和可追踪。本教程将从 Redux 的基本概念讲起,逐步深入到与 React 结合使用的最佳实践,以及如何处理异步操作和调试。

Redux 简介

Redux 允许你将应用的状态集中存储在一个全局的 store 中,并通过纯函数 reducer 来描述状态的变化。这种集中式的状态管理方式简化了组件间的通信,并且易于调试。

为什么使用 Redux?

  • 独立性:Redux 可以独立于框架运行,不仅限于 React。
  • 简化通信:无视组件层级,简化组件间的数据传递。
  • 数据流清晰:单向数据流使得状态变更易于追踪和定位问题。
  • 调试友好:配套的调试工具如 Redux DevTools 提供了强大的调试支持。

Redux 快速体验

实现一个计数器

不依赖任何框架或构建工具,使用纯 JavaScript 和 Redux API 来实现一个简单的计数器。

  1. 定义 reducer 函数,根据 action 返回新的状态。
  2. 使用 createStore 创建 store 实例。
  3. 使用 subscribe 订阅状态变化。
  4. 使用 dispatch 提交 action,触发状态变更。
  5. 使用 getState 获取最新状态并更新视图。
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button><script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script> <script>// 1. 定义reducer函数 // 作用: 根据不同的action对象,返回不同的新的state// state: 管理的数据初始状态// action: 对象 type 标记当前想要做什么样的修改function reducer (state = { count: 0 }, action) {// 数据不可变:基于原始状态生成一个新的状态if (action.type === 'INCREMENT') {return { count: state.count + 1 }}if (action.type === 'DECREMENT') {return { count: state.count - 1 }}return state}// 2. 使用reducer函数生成store实例const store = Redux.createStore(reducer)// 3. 通过store实例的subscribe订阅数据变化// 回调函数可以在每次state发生变化的时候自动执行store.subscribe(() => {console.log('state变化了', store.getState())document.getElementById('count').innerText = store.getState().count})// 4. 通过store实例的dispatch函数提交action更改状态 const inBtn = document.getElementById('increment')inBtn.addEventListener('click', () => {// 增store.dispatch({type: 'INCREMENT'})})const dBtn = document.getElementById('decrement')dBtn.addEventListener('click', () => {// 减store.dispatch({type: 'DECREMENT'})})// 5. 通过store实例的getState方法获取最新状态更新到视图中</script>

Redux 数据流架构

Redux 的数据流向清晰,从 stateaction,再到 reducer,最后回到新的 state

Redux 与 React 环境准备

虽然 Redux 可以独立于 React 使用,但通常我们会将其与 React 结合,利用 react-redux 库来连接 Redux 和 React 组件。

配套工具

  • Redux Toolkit (RTK):官方推荐的编写 Redux 逻辑的方式,简化代码。
  • react-redux:连接 Redux 和 React 的桥梁。

配置基础环境

使用 Create React App (CRA) 创建项目,并安装必要的依赖。

npx create-react-app react-redux 
npm i @reduxjs/toolkit  react-redux 
npm run start 

Redux 与 React 实现计数器

使用 Redux Toolkit 创建 Store

利用 createSlice API 简化 slice 的创建,包括初始状态、reducers 和 actions。

//store/modules/conterStore.js
import { createSlice } from '@reduxjs/toolkit'const counterStore = createSlice({// 模块名称独一无二name: 'counter',// 初始化数据stateinitialState: {count: 1},// 修改数据的同步方法reducers: {increment (state) {state.count++},decrement(state){state.count--}}
})// 解构出actionCreater函数
const { increment, decrement } = counterStore.actions// 获取reducer函数
const counterReducer = counterStore.reducer// 以按需导出的方式导出actionCreater函数和reducer函数
export { increment, decrement }
// 以默认导出的方式导出reducer函数
export default counterReducer

为 React 注入 Store

使用 Provider 组件将 store 传递给 React 应用,建立连接。

// 项目的src/index.js中
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'ReactDOM.createRoot(document.getElementById('root')).render(// 提供store数据<Provider store={store}><App /></Provider>
)

React 组件使用 Store 数据

使用 useSelector 钩子从 store 中读取数据,使用 useDispatch 钩子分发 actions。

//app.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';function App() {const count = useSelector(state => state.counter.count);return(<div className="App">{count}</div>);
}export default App;

React 组件修改 Store 中的数据

使用 useDispatch 钩子生成提交 action 对象的 dispatch 函数。

//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement } from './store/modules/counterStore';
function App() {const count = useSelector(state => state.counter.count);const dispatch = useDispatch();return(<div className="App"><button onClick={() => dispatch(increment())}>Increment</button>{count}<button onClick={() => dispatch(decrement())}>Decrement</button></div>);
}export default App;

Redux 与 React 提交 Action 传参

在 action 创建函数中添加参数,允许在 dispatch 时传递额外的数据。

//conterStore.js
import { createSlice } from '@reduxjs/toolkit'const counterStore = createSlice({// 模块名称独一无二name: 'counter',// 初始化数据stateinitialState: {count: 0},// 修改数据的同步方法reducers: {increment (state) {state.count++},decrement(state){state.count--},addToNum(state, action){state.count += action.payload}}
})// 解构出actionCreater函数
const { increment, decrement, addToNum } = counterStore.actions// 获取reducer函数
const reducer = counterStore.reducer// 以按需导出的方式导出actionCreater函数和reducer函数
export { increment, decrement, addToNum }
// 以默认导出的方式导出reducer函数
export default reducer
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, addToNum } from './store/modules/counterStore';function App() {const count = useSelector(state => state.counter.count);const dispatch = useDispatch();return (<div className="App"><button onClick={() => dispatch(increment())}>Increment</button>{count}<button onClick={() => dispatch(decrement())}>Decrement</button><button onClick={() => dispatch(addToNum(10))}>Add 10</button><button onClick={() => dispatch(addToNum(20))}>Add 20</button></div>);
}export default App;

Redux 异步 Action 处理

封装异步逻辑,如 API 请求,并在获取数据后 dispatch 同步 action 更新状态。

//store/modules/channelStore.js
import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'const channelStore = createSlice({name: 'channel',initialState: {channelList: []},reducers: {setChannelList (state, action) {state.channelList = action.payload}}
})// 创建异步
const { setChannelList } = channelStore.actions
const url = 'http://geek.itheima.net/v1_0/channels'
// 封装一个函数 在函数中return一个新函数 在新函数中封装异步
// 得到数据之后通过dispatch函数 触发修改
const fetchChannelList = () => {return async (dispatch) => {const res = await axios.get(url)dispatch(setChannelList(res.data.data.channels))}
}export { fetchChannelList }const channelReducer = channelStore.reducer
export default channelReducer//store/index.js
import { configureStore } from '@reduxjs/toolkit'import counterReducer from './modules/counterStore'
import channelReducer from './modules/channelStore'const store = configureStore({reducer: {counter: counterReducer,channel: channelReducer}
})export default store
//App.js
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { increment, decrement, addToNum } from './store/modules/counterStore';
import { fetchChannelList } from './store/modules/channelStore';
import { useEffect } from 'react';function App() {const count = useSelector(state => state.counter.count);const {channelList} = useSelector(state => state.channel);const dispatch = useDispatch();//使用useEffect触发异步请求执行useEffect(() => {dispatch(fetchChannelList());}, [dispatch]);return (<div className="App"><button onClick={() => dispatch(increment())}>Increment</button>{count}<button onClick={() => dispatch(decrement())}>Decrement</button><button onClick={() => dispatch(addToNum(10))}>Add 10</button><button onClick={() => dispatch(addToNum(20))}>Add 20</button><ul>{channelList.map(task => <li key={task.id}>{task.name}</li>)}</ul></div>);
}export default App;

Redux 调试 - DevTools

使用 Redux DevTools 进行状态管理和 action 的调试,提供实时的 state 展示和 action 追踪。

结语

Redux 提供了一种强大且可预测的方式来管理应用的状态。通过本教程,你已经了解了 Redux 的基本概念、如何在 React 中使用 Redux,以及如何处理异步操作和调试。希望这能帮助你更好地构建和管理复杂的前端应用状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/876556.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

滑动窗口练习6-找到字符串中所有字母异位词

题目链接&#xff1a;**. - 力扣&#xff08;LeetCode&#xff09;** 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#…

《程序猿入职必会(6) · 返回结果统一封装》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

Profinet从站转TCP/IP协议转化网关(功能与配置)

如何将Profinet和TCP/IP网络连接通讯起来呢?近来几天有几个朋友问到这个问题&#xff0c;那么作者在这里统一说明一下。其实有一个不错的设备产品可以很轻易地解决这个问题&#xff0c;名为JM-DNT-PN。接下来作者就从该设备的功能及配置详细说明一下。 一&#xff0c;设备主要…

el-table合计行更新问题

说明&#xff1a;在使用el-table自带的底部合计功能时&#xff0c;初始界面不会显示合计内容 解决方案&#xff1a;使用 doLayout()方法 updated() {this.$nextTick(() > {this.$refs[inventorySumTable].doLayout();});},完整代码&#xff1a; // show-summary&#xff1a…

Bugku的web题目get,post

1.web基础$_GET http://114.67.175.224:17587/ OK明显的代码审计题目。 让我们看看代码&#xff0c;先get获取what参数变量&#xff0c;如果what变量‘flag’&#xff0c;输出flag。 该题为GET传参&#xff0c;可直接在url后面加参数 在url后加上?whatflag 即可获得flag 2…

科普文:科普文:springcloud之-Hystrix服务容错

Hystrix概念 Hystrix 服务容错保护 的概念和说明 这就是大名鼎鼎的&#xff1a;豪猪 豪猪的英文就是&#xff1a;Hystrix&#xff0c;国外一些大牛的程序员在给自己的架构起名字的时候&#xff0c;往往就这么特别。哪天咱们中国人自己也能写出些架构&#xff0c;咱们就按照中…

2024后端开发面试题总结

一、前言 上一篇离职贴发布之后仿佛登上了热门&#xff0c;就连曾经阿里的师兄都看到了我的分享&#xff0c;这波流量真是受宠若惊&#xff01; 回到正题&#xff0c;文章火之后&#xff0c;一些同学急切想要让我分享一下面试内容&#xff0c;回忆了几个晚上顺便总结一下&#…

【VS2019安装+QT配置】

【VS2019安装QT配置】 1. 前言2. 下载visual studio20193. visual studio2019安装4. 环境配置4.1 系统环境变量配置4.2 qt插件开发 5. Visual Studio导入QT项目6. 总结 1. 前言 前期安装了qt&#xff0c;发现creator编辑器并不好用&#xff0c;一点都不时髦。在李大师的指导下&…

C++画蜡烛图

GPT-4o (OpenAI) 在 C 中绘制蜡烛图通常不像在高级语言&#xff08;如 Python&#xff09;中那么简单&#xff0c;因为 C 并没有内置的图形绘制库。然而&#xff0c;您可以使用一些第三方库来完成这项任务&#xff0c;比如使用 Qt 或者 SFML 等图形库。这里我们以 Qt 库为例&a…

PM2 快速上手指南

PM2是 Node.js 的优秀运行时管理工具&#xff0c;专为简化和优化 Node.js 应用程序的生产部署与运行而设计。 PM2 官网链接: https://pm2.keymetrics.io/ 1.PM2 的优势 持续运行&#xff1a;即使应用出错或崩溃&#xff0c;也能自动重启。负载均衡&#xff1a;智能地自动分…

Linux shell编程学习笔记67: tracepath命令 追踪数据包的路由信息

0 前言 网络信息是电脑网络信息安全检查中的一块重要内容&#xff0c;Linux和基于Linux的操作系统&#xff0c;提供了很多的网络命令&#xff0c;今天我们研究tracepath命令。 Tracepath 在大多数 Linux 发行版中都是可用的。如果在你的系统中没有预装&#xff0c;请根据你的…

WordPress插件介绍页源码单页Html

源码介绍 WordPress插件介绍页源码单页Html源码&#xff0c;这是一款产品介绍使用页面&#xff0c;也可以用来做其他软件或者应用介绍下载页&#xff0c;界面简约美观&#xff0c;源码由HTMLCSSJS组成&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器…

合作伙伴中心Partner Center中添加了Copilot预览版

目录 一、引言 二、Copilot 功能概述 2.1 Copilot 简介 2.2 Copilot 的核心功能 2.3 Copilot 的访问和使用 三、Copilot 的使用方法 3.1 Copilot 功能区域 3.2 Copilot 使用示例 3.2.1 编写有效提示 3.2.2 使用反馈循环 四、负责任的人工智能 4.1 Copilot 结果的可…

UE4如何直接调试Game

某些功能在编辑器里不好调试&#xff0c;例如Pak&#xff0c;就需要直接调试 Game&#xff0c;做法是选择 Game&#xff0c;不要选择Client&#xff0c;加断点&#xff0c;然后点击 Debug 就好了。 断点调试成功&#xff1a; 同时看到界面&#xff1a;

PCIe总线-Linux内核PCIe软件框架分析(十一)

1.简介 Linux内核PCIe软件框架如下图所示&#xff0c;按照PCIe的模式&#xff0c;可分为RC和EP软件框架。RC的软件框架分为五层&#xff0c;第一层为RC Controller Driver&#xff0c;和RC Controller硬件直接交互&#xff0c;不同的RC Controller&#xff0c;其驱动实现也不相…

【React】详解 React Hooks 使用规则

文章目录 一、Hooks 的基本原则1. 只在最顶层调用 Hooks2. 只在 React 函数组件和自定义 Hooks 中调用 Hooks 二、常见 Hooks 及其使用规则1. useState2. useEffect3. useContext4. useReducer5. useMemo6. useCallback 三、常见错误及其解决方案1. 在条件语句中调用 Hooks2. 在…

RK3568 Linux 平台开发系列讲解(内核入门篇):从内核的角度看外设芯片的驱动

在嵌入式 Linux 开发中,外设芯片的驱动是实现操作系统与硬件之间交互的关键环节。对于 RK3568 这样的处理器平台,理解如何从内核的角度构建和管理外设芯片的驱动程序至关重要。 1. 外设驱动的基础概念 外设驱动(Device Driver)是操作系统与硬件设备之间的桥梁。它负责控…

机器学习(二十一):错误分析、创造数据和迁移学习

一、错误分析 假设交叉验证集一共有500个数据点&#xff0c;模型拟合结果中&#xff0c;有100个数据点有误。 错误分析就是&#xff0c;手动地分析这100个错误数据&#xff08;或随机选择一些错误数据&#xff09;&#xff0c;根据它们的共同属性、共同特征分类&#xff0c;然…

在QT中使用多线程并发服务器(C++)

什么是多线程并发服务器&#xff1f;在QT里如何使用多线程并发服务器呢&#xff1f; 多线程并发服务器是一种网络服务器设计&#xff0c;它能够同时处理多个客户端的请求。在多线程服务器中&#xff0c;主线程负责监听和接受来自客户端的连接请求&#xff0c;每当有一个新的连…

C++(week13): C++基础: 标准模板库 STL

文章目录 零、标准模板库 STL一、容器 (Container)1.序列式容器(1)vector2.五种遍历10.vector的迭代器失效问题 (2)deque(3)list 2.关联式容器(1)set4.set的查找(2)find() 8.set中存储自定义类型&#xff1a;三种方法 (2)multiset7.multiset的特殊操作&#xff1a;bound系列函数…