第三十三章 使用Redux管理状态

简介: 第三十三章 使用Redux管理状态

  Redux(全称为Redux)是一个基于状态管理的JavaScript库,它可以用来构建可重用的、可维护的代码。Redux主要用于处理复杂的应用程序中的状态管理,它能够自动地处理应用程序中的更改,并在需要时更新视图。

  Redux使用一种被称为“状态树”的数据结构来管理应用程序的状态。状态树中的每个节点都代表应用程序中的一个状态,而每个状态都可以有自己的副本和父节点。当应用程序中的某个状态发生更改时,它会通过一个被称为“动作”的API来更新状态树,并触发有关的副作用。这意味着,当一个部分的状态更改时,不会影响其他部分。

  Redux使用一个中央控制器来协调应用程序中的所有状态更改。这个控制器可以监听所有的动作,并根据需要更改状态。Redux也提供了一些工具,如树形状态视图和状态转换器,来帮助开发人员处理复杂的状态管理。

  总的来说,Redux是一种非常强大和灵活的库,可以用来构建复杂的应用用程序。它能够使开发人员专注于应用程序的逻辑,并帮助他们更轻松地管理应用程序中的状态。

具体详情请参考:Redux 中文官网

什么情况下使用Redux

Redux 通常用于处理复杂的应用程序,特别是那些具有大量状态和复杂逻辑的应用程序。以下是一些 Redux 可能适合您的应用程序的示例:

  • 您的应用程序具有复杂的路由和状态管理逻辑。
  • 您需要处理大量的状态和更改,并且不希望在代码中硬编码它们。
  • 您需要更好地组织和管理您的状态和副作用。
  • 您需要一种方式来组合和重用不同的状态和动作。

当您考虑使用 Redux 时,需要确定您的应用程序是否适合使用 Redux。如果您的应用程序符合上述条件,那么 ReducerMiddlewareother 组件可以帮助您更轻松地管理应用程序的状态和逻辑。


Redux的工作原理

Redux 的工作原理是通过将应用程序的状态和动作拆分成独立的组件来实现的。在 Reducer 中,您可以使用一组函数来更改应用程序的状态。每个函数都接收一个 state 和一个 action,并返回一个新的 state。Redux 通过组合这些 state 来控制应用程序的状态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HeKPRhDI-1684308971073)(D:\GeniusNotes\ReactNote\react_staging\img\redux原理图.png)]


Redux 中,还有一些中间件(称为 Connect),它们用于协调 Redux 中的组件。Connect 可以帮助您将应用程序的不同部分连接在一起,并提供一些功能,如数据流和状态管理。 当 Redux 中的某个组件触发一个动作时,它会通过 Action 来通知 Redux

这个 Action 可以被 Redux 中的其他组件所读取和处理。Redux 还提供了一些其他的工具和库,如 ConnectHooks Reducer,来帮助开发人员处理复杂的应用程序逻辑。

Redux的三个核心概念


  1. ActionAction 是一种数据类型,它表示一个状态更改操作。Action 可以被 Redux 中的所有组件读取和处理。Action 可以包含任何类型的数据,例如字符串、数字或映射。
  2. ReducerReducerRedux 中用于修改 state 的组件。Reducer 接收一个 state 和一个 action,并返回一个 new stateReducer 的作用是通过更改状态来控制 Redux 中的应用程序逻辑。Reducer 通常由一组函数组成,这些函数接收一个 state 和一个 action,并返回一个 new state
  3. Store:Store 是 Redux 中用于存储和管理 state 和动作的组件。Store 是一个全局的对象,它可以存储应用程序的所有 state 和动作。Store 可以被 Redux 中的其他组件所读取和处理。Store 还负责协调 Redux 中的所有 state 的更改。

通过求和案例循序渐进了解Redux

以下是案列的图示:

该组件(包含一个下拉框,四个功能按钮)功能很简单:

  • 选择下拉框,选择要加减的具体数值
  • 点击按钮+,将下拉框的数值进行相加
  • 点击按钮-,将下拉框的数值进行相减
  • 点击按钮当前求和为奇数为,当和是奇数的时候才将下拉框的值进行相加,否则不操作
  • 点击按钮异步加,点击按钮之后,隔一段时间才将下拉框的值进行相加

1、使用普通的react程序实现该功能

文件:components/Count/index.jsx

import React, { Component } from 'react'
export default class Count extends Component {
  state = { count: 0 }
  increment = () => {
    // 普通加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、获取原来状态里面的值
    const { count } = this.state
    // 3、进行计算并更新状态值
    this.setState({ count: count + value * 1 })
  }
  decrement = () => {
    // 普通减
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、获取原来状态里面的值
    const { count } = this.state
    // 3、进行计算并更新状态值
    this.setState({ count: count - value * 1 })
  }
  incrementIfOdd = () => {
    // 当前求和为奇数为
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、获取原来状态里面的值
    const { count } = this.state
    // 3、进行计算并更新状态值
    if (store.getState() % 2 !== 0) {
      this.setState({ count: count + value * 1 })
    }
  }
  incrementAsync = () => {
    // 异步加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、获取原来状态里面的值
    const { count } = this.state
    // 3、进行计算并更新状态值
    setTimeout(() => {
     this.setState({ count: count + value * 1 })
    }, 500)
  }
  render() {
    return (
      <div>
        <h1>当前求和为:{this.state.count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数为</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}

文件:App.jsx

import React, { Component } from 'react'
import Count from './components/Count'
export default class App extends Component {
  render() {
    return (
      <div>
        <Count/>
      </div>
    )
  }
}

以上代码就是通过普通的state实现的案例功能。


2、Redux精简版改进求和案例
  • 步骤1:安装依赖包
npm i redux
  • 步骤2:创建文件:redux/store.js
/** 该文件专门用于暴露store对象,整个应用只有一个store对象 */
// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from 'redux'
// 引入为Count组件服务的reducer
import countReducer from './count_reducer'
// 暴露store
export default createStore(countReducer)
  • 步骤3:创建文件:redux/count_reducer.js
/**
 * 1、该文件是用于创建一个为count组件服务的reducer,reducer的本质就是一个函数
 * 2、reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
 */
const initCount = 0  // 当reducer做初始化的时候,将初始值undefined改为0
export default function countReducer( preState = initCount, action) {
  // 从action对象中获取type和data
  const { type, data } = action;
  // 根据type类型,如何加工数据
  switch (type) {
    case 'increment': // 如果是加,使用以下逻辑加工数据
      return preState + data;
    case 'decrement': // 如果是减,使用以下逻辑加工数据
    return preState - data;
    default:
      return preState ;
  }
}
  • 步骤4:修改Count组件逻辑
import React, { Component } from 'react'
import store from '../../redux/store' // 引入store
export default class Count extends Component {
  // state = { count: 0 } // 使用了redux的状态后,就不需要组件内部的state了
  componentDidMount() {
    // 监测redux中状态的变化,只要变化,就调用render
    store.subscribe(()=>{
      this.setState({}) // 当调用这个方法后,页面才会更新
    })
  }
  increment = () => {
    // 普通加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值,通知redux执行相加逻辑
    store.dispatch({type:'increment',data: value * 1})
  }
  decrement = () => {
    // 普通减
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值,通知redux执行相减逻辑
    store.dispatch({type:'decrement',data: value * 1})
  }
  incrementIfOdd = () => {
    // 当前求和为奇数为
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    if (store.getState() % 2 !== 0) {
      // 通知redux执行相加逻辑
      store.dispatch({type:'increment',data: value * 1})
    }
  }
  incrementAsync = () => {
    // 异步加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    setTimeout(() => {
      // 通知redux执行相加逻辑
      store.dispatch({type:'increment',data: value * 1})
    }, 500)
  }
  render() {
      // store.getState(),这个API用于获取redux的状态
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数为</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}
  • 步骤5:优化redux的监听

为了让整个应用都监听redux的变化,我们需要将订阅redux变化的逻辑写到入口文件中

文件:index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './redux/store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
// 监听全局redux,一但发生改变就会更新页面
store.subscribe(()=> {
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
  );
})
  • 小节总结

(1). 去除Count组件自身的状态

(2). src下建立:

-src
|----redux
   |----store.js
   |----count_reducer.js

(3).store.js:

1).引入redux中的createStore函数,创建一个store

2).createStore调用时需要传入一个为其服务的reducer

3).记得暴露store对象

(4).count_reducer.js:

1). reducer的本质是一个函数,接收:preState,action,返回加工后的状态

2). reducer有两个作用:初始化状态,加工状态


3). reducer被第一次调用时,是store自动触发的,传递的preStateundefined,传递的action是:{type:'@@REDUX/INIT_a.1.2.3'}

(5).在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>

备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。


3、Redux完整版求和案例

完整版比精简版多了几个文件:constant.jscount_action.js

  • 定义常量文件:constant.js,便于管理
// 该模块是用于定义action对象的type类型的常量值
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
  • 定义管理action对象的文件,层次分明
// 该文件专门为Count组件生成action对象
import {INCREMENT,DECREMENT} from './constant'
export const createIncrementAction = data => ({ type: INCREMENT,data})
export const createDecrementAction = data => ({ type: DECREMENT,data})
  • 完善组件内容
import React, { Component } from 'react'
import store from '../../redux/store'
// 引入actioncreator,专门用于创建action对象
import { createIncrementAction, createDecrementAction} from '../../redux/count_action'
export default class Count extends Component {
  increment = () => {
    // 普通加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    // 通知redux更改状态
    store.dispatch(createIncrementAction(value * 1))
  }
  decrement = () => {
    // 普通减
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    // 通知redux更改状态
    store.dispatch(createDecrementAction(value * 1))
  }
  incrementIfOdd = () => {
    // 当前求和为奇数为
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    if (store.getState() % 2 !== 0) {
      // 通知redux更改状态
      store.dispatch(createIncrementAction(value * 1))
    }
  }
  incrementAsync = () => {
    // 异步加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    setTimeout(() => {
      // 通知redux更改状态
      store.dispatch(createIncrementAction(value * 1))
    }, 500)
  }
  render() {
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数为</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}
  • 小节总结

新增文件:

(1). count_action.js:专门为Count组件服务创建action对象。

(2). constant.js:放置容易写错的type值。

4、Redux中开启异步action

Redux中,同步Action和异步Action的主要区别在于它们如何触发dispatch

同步Action: 当一个同步Action被创建时,它会立即被发送到store中,并等待store更新状态。如果有其他组件正在使用store,那么这些组件也会立即更新状态。因此,同步Action通常用于那些不需要等待的、简单的操作。

异步Action: 异步Action会在发出后继续执行其他操作,直到完成后再将结果发送到store中。这意味着,如果有其他组件正在使用store,那么它们可能不会立即看到更新的状态。因此,异步Action通常用于那些需要等待的操作。

  • 步骤1:编写一个异步的action的函数

普通的action返回的是一个Object的一般对象,而异步action返回的是一个函数,因为只有在函数里面才能进行异步操作。

文件:redux/count_action.js

// 该文件专门为Count组件生成action对象
import {INCREMENT,DECREMENT} from './constant'
// 一般action,返回的是一般对象
export const createIncrementAction = data => ({ type: INCREMENT,data})
export const createDecrementAction = data => ({ type: DECREMENT,data})
// 异步action,返回的是一个函数
export const createIncrementAsyncAction = (data,time) => {
    // 在返回的函数的参数是一个dispatch,用于我们处理完异步任务后分发action对象
    return (dispatch) => { 
      setTimeout(() => {
        dispatch(createIncrementAction(data))
      }, time);
    }
}
  • 步骤2:引入中间件来支持异步action

由于store分发action的时候,它只认普通对象,如果要接收一个函数就需要使用到一个中间件。

(1). 安装依赖redux-thunk

npm i redux-thunk

(2). 修改redux/store.js文件

// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore , applyMiddleware} from 'redux'
import countReducer from './count_reducer'
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
export default createStore(countReducer,applyMiddleware(thunk))
  • 步骤3:在Count组件里面使用异步action
import React, { Component } from 'react'
import store from '../../redux/store'
// 引入actioncreator,专门用于创建action对象
import {  createIncrementAsyncAction} from '../../redux/count_action'
export default class Count extends Component {
 // ...
  incrementAsync = () => {
    // 异步加
    // 1、获取用户选择的值
    const { value } = this.selectNumber
    // 2、进行计算并更新状态值
    store.dispatch(createIncrementAsyncAction(value*1,500))
  }
  render() {
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数为</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    )
  }
}
  • 小节总结
    (1).明确:延迟的动作不想交给组件自身,想交给action
    (2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回
    (3).具体编码:

1). npm i redux-thunk ,并配置在store

2). 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务

3). 异步任务有结果后,分发一个同步的action去真正操作数据。

4). 备注:异步任务action不是必须要写的,完全可以自己等待异步任务的结果完了,再去分发同步action


相关文章
|
前端开发 Java 关系型数据库
超市商品管理系统的设计与实现(论文+源码)_kaic
超市商品管理系统的设计与实现(论文+源码)_kaic
|
9月前
|
人工智能 自然语言处理 IDE
Trae 开发工具与使用技巧
V哥推荐字节推出的AI原生IDE——Trae,这款工具大幅提升程序员开发效率。Trae定位为“AI协同编程”伙伴,支持零基础用户通过对话完成项目开发。其核心功能包括Builder模式自动生成代码、智能问答辅助开发、上下文引用与多模态开发等。对比Cursor和Windsurf,Trae在中文支持、全自动项目管理和免费模型使用上更具优势。新手可通过3步快速上手:启动Builder模式、一键运行调试、迭代优化。立即体验Trae,开启AI时代编程新篇章!
2475 2
|
6月前
|
Ubuntu 安全 API
Python3.14正式支持Free Threaded版本!
Python 社区迎来历史性时刻!Python 3.14 正式将无 GIL 构建列为受支持选项,标志着 Free‑Threaded Phase II 启动。本文将深入解析 PEP 779 规定的支持标准、3.14.0b3 版本的新变化,以及这对Python开发者意味着什么。文末还有小彩蛋哦!
967 87
|
8月前
|
存储 传感器 人工智能
《深度剖析:分布式软总线双轮驱动机制下传输效率与可靠性的精妙平衡》
分布式软总线是实现智能设备互联互通的核心技术,其双轮驱动机制通过优化数据传输与确认流程,大幅提升传输效率和可靠性。在智能家居场景中,它让智能音箱、摄像头、家电等设备紧密协作;在智能办公领域,则助力多设备无缝连接与高效协同。双轮驱动机制采用智能流量控制策略,动态调整发送窗口和速率,适应复杂网络环境及不同设备性能需求。未来结合AI与新一代通信技术,将进一步提升万物互联体验。
256 3
|
9月前
|
5G 网络架构 UED
网速只拼Mbps?解码网速真相的五大关键因素
Mbps(兆比特每秒)是衡量数据传输速度的单位,表示每秒传输的百万比特数。它是评估网络性能的核心指标,广泛应用于家用宽带、移动网络和企业级网络中。Mbps 数值越高,理论上数据传输越快,但实际体验还受网络拥塞、丢包率和信号强度等因素影响。例如,在网络高峰时段或信号较弱的地方,即使Mbps数值高,也可能出现卡顿。5G和光纤技术显著提升了Mbps速率,但仍需考虑硬件设备如路由器和网卡的性能瓶颈。理解Mbps及其影响因素,有助于用户选择合适的网络服务并优化网络体验。
1030 1
|
9月前
|
数据可视化 前端开发 JavaScript
地图可视化的艺术:深入比较Mapbox、OpenLayers、Leaflet和Cesium,不同场景下应如何选择地图库
选择合适的地图库取决于项目的需求、团队的技术栈以及预算等因素。简单来说,新手可以从leaflet入手;GIS开发使用openlayers会更顺手一些;mapbox适应大多数2D和2.5D场景,可视化效果好,但是不开源;cesium更侧重于3D场景。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出
|
安全 网络协议 关系型数据库
最好用的17个渗透测试工具
渗透测试是安全人员为防止恶意黑客利用系统漏洞而进行的操作。本文介绍了17款业内常用的渗透测试工具,涵盖网络发现、无线评估、Web应用测试、SQL注入等多个领域,包括Nmap、Aircrack-ng、Burp Suite、OWASP ZAP等,既有免费开源工具,也有付费专业软件,适用于不同需求的安全专家。
2331 2
|
前端开发 JavaScript Java
基于SpringBoot实现功能最全电影购票与信息资讯平台
基于SpringBoot实现功能最全电影购票与信息资讯平台
254 1
基于SpringBoot实现功能最全电影购票与信息资讯平台
|
监控 数据可视化 Java
【JAVA】分布式链路追踪技术概论
skywalking拥有更加的强大和细粒度的图形监控界面。
318 2
|
存储 缓存 安全
Java并发基础:ConcurrentLinkedDeque全面解析!
ConcurrentLinkedDeque类提供了线程安全的双端队列操作,支持高效的并发访问,因此在多线程环境下,可以放心地在队列的两端添加或移除元素,而不用担心数据的一致性问题。同时,它的内部实现采用了无锁算法,从而避免了传统锁带来的性能开销。
420 2
Java并发基础:ConcurrentLinkedDeque全面解析!