第三十三章 使用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


相关文章
|
6天前
|
JavaScript
状态管理(pinia)
状态管理(pinia)
15 0
|
6天前
|
JavaScript 前端开发 数据管理
第三十四章 使用react-redux进一步管理状态
第三十四章 使用react-redux进一步管理状态
|
7月前
|
JavaScript API
Vuex状态管理最佳实践
使用Vuex进行状态管理时,有一些最佳实践可以帮助你保持代码清晰、可维护和高效。以下是一些详细的Vuex状态管理最佳实践
100 3
|
6天前
|
存储 JavaScript 前端开发
【第36期】一文学会Redux状态管理
【第36期】一文学会Redux状态管理
70 0
|
6天前
|
JavaScript 测试技术
状态管理:集成 Vuex 进行全局状态管理
【4月更文挑战第22天】Vuex 是 Vue.js 的状态管理库,通过状态、mutations、actions 和 modules 等核心概念集中管理应用状态。创建 store,划分模块以增强代码维护性。mutations 同步改变状态,actions 处理异步逻辑。遵循 Vuex 规范,在组件中使用辅助函数访问状态。有效更新和处理错误,实现与其它工具集成,提升应用性能和可靠性。注意根据项目需求灵活使用,防止状态管理过度复杂。
|
6天前
|
JavaScript 前端开发
【干货分享】轻松入门 Redux:解密状态管理的神奇世界
【干货分享】轻松入门 Redux:解密状态管理的神奇世界
|
6天前
|
资源调度 JavaScript
如何使用 Vuex 管理应用的状态?
如何使用 Vuex 管理应用的状态?
18 3
|
6天前
|
JavaScript 程序员
状态管理之Vuex (二) 异步管理
状态管理之Vuex (二) 异步管理
17 0
|
6天前
|
存储 JavaScript 前端开发
Vue状态管理:Vue中的状态管理是什么?为什么使用状态管理?
Vue状态管理:Vue中的状态管理是什么?为什么使用状态管理?
125 2