详解Redux的工作流程

目录
  • Redux理解
    • redux是什么
    • 什么情况下需要使用redux?
    • redux工作流程图
    • action
    • reducer
    • store
  • 求和案例——纯react版
  • 求和案例——redux精简版
  • 求和案例——redux完整版

(当然不是这张图)

Redux理解

redux是什么

  • redux是一个专门用于做状态管理的JS库(不是react插件库);
  • 它可以在react、angular、vue等项目中,但基本与react配合使用;
  • 作用:集中式管理react应用中多个组件共享的状态。

什么情况下需要使用redux?

  • 某个组件的状态,需要让其他组件可以随时拿到(共享);
  • 一个组件需要改变另一个组件的状态(通信);
  • 总体原则:能不用就不用,如果不用比较吃力才考虑使用。

redux工作流程图

action

  • 动作的对象
  • 包含两个属性:
    • type:标识属性,值为字符串,唯一,必要属性;
    • data:数据属性,值类型任意,可选属性。
  • 例子:{ type:‘ADD_STUDENT’, data:{name: ‘tom’,age: 18} }

reducer

  • 用于初始化状态、加工状态;
  • 加工时,根据旧的 state 和 action,产生新的 state 的纯函数;
  • 初始化时,previousState 为 undefined。

store

  • 将state 、action 、reducer联系在一起的对象;
  • 如何得到此对象?

如何得到此对象?

import { createStore } from 'redux'
//
import reducer from './reducers'
//
const store = createStore(reducer)

3.此对象的功能?

  • getState():得到 state;
  • dispatch(action):分发 action,触发 reducer 调用,产生新的 state;
  • subscribe(listener):注册监听,当产生了新的 state 时,自动调用。

select选择框内有三个数组1、2、3可选,选择1后点击“加”即和加1;
第三个按钮为和为奇数时,则加;和为偶数时,则不变;
最后的“异步加”则是用setTimeout模拟了一个异步环境,点击“异步加”等待0.5s再加。

求和案例——纯react版

项目目录:

src
├─App.jsx
├─index.js
├─components
|     ├─Count
|     |   └index.jsx
public
└index.html

使用create-react-app创建脚手架项目,项目目录如上所示。

纯react版是通过本组件的state获取数据,在此不过多赘述。
欢迎订阅本专栏【React–从基础到实战】学习react知识,持续更新中!

App.jsx

import React, { Component } from 'react'
import Count from './components/Count'

export default class App extends Component {
  render() {
    return (
      <div>
        <Count />
      </div>
    )
  }
}

src/component/Count/index.jsx

import React, { Component } from 'react'

export default class Count extends Component {

  state = { count: 0 }

  // 加法
  increment = () => {
    const { value } = this.selectedNumber
    const { count } = this.state
    this.setState({ count: count + parseInt(value) });
  }
  // 减法
  decrement = () => {
    const { value } = this.selectedNumber
    const { count } = this.state
    this.setState({ count: count - value });
  }
  // 和为奇数时,加
  incrementIfOdd = () => {
    const { value } = this.selectedNumber
    const { count } = this.state
    // 除以2取余数,余数为0代表和为偶数,不加;余数不为0,代表和为奇数,加
    if (count % 2 !== 0) {
      this.setState({ count: count + parseInt(value) });
    }
  }
  // 异步加
  incrementAsync = () => {
    const { value } = this.selectedNumber
    const { count } = this.state
    setTimeout(() => {
      this.setState({ count: count + parseInt(value) });
    }, 500)
  }

  render() {
    return (
      <div>
        <h1>当前求和为:{this.state.count}</h1>
        <select ref={currentNode => { this.selectedNumber = currentNode }}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;&nbsp;&nbsp;
        <button onClick={this.increment}>加</button>&nbsp;
        <button onClick={this.decrement}>减</button>&nbsp;
        <button onClick={this.incrementIfOdd}>和为奇数时,加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    )
  }
}

求和案例——redux精简版

为了让大家更好的理解redux基本原理,我们先仅使用少数的api,用过一个精简版的redux去完成这个求和案例,最后我们会用完整版的redux去完成这个求和案例。学东西嘛,得循序渐进,不能一口吃成大胖子不是?

首先,我们需要下载redux:

npm install redux
或
yarn add redux

根据原理图,我们必不可少的肯定是需要store这个对象,以及reducer这个函数的。

store对象负责存储state,reducer负责初始化和加工state,都是必不可少的。

1、创建store.js

/*
  该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
// 引入createStore,专门用于创建redux中最为核心的store对象
import { legacy_createStore as createStore } from 'redux';
// 引入为Count组件服务的reducer
import countReducer from './count_reducer'
// 暴露store
export default createStore(countReducer)

2、创建count_reducer.js

/*
  1.该文件用于创建一个为Count组件服务的reducer函数
  2.reducer函数会接收到两个参数,分别为之前的状态(prevState)和动作对象(action)
*/
// 初始化state,prevState=initState含义是:给形参prevState赋初始值
const initState = 0
export default function countReducer(prevState = initState, action) {
  console.log(prevState, action)
  // 从action对象中获取type、data
  const { type, data } = action
  // 根据type的值决定如何加工数据
  switch (type) {
    case 'increment':
      return prevState + data;
    case 'decrement':
      return prevState - data;
    default:
      return prevState
  }
}

3、回到Count组件中,我们先试着获取到store中存储的数据。

//  引入store,用于获取redux中保存的状态
import store from '../../redux/store'
···
render() {
    return (
      <div>
      // <h1>当前求和为:{this.state.count}</h1>
        <h1>当前求和为:{store.getState()}</h1>
···

现在我们把状态(state)交给redux管理了,当然就不需要在Count组件中初始化状态了,也就不需要再到this.state中结构赋值count变量了。

我们只需要调用redux的api:store.getState()就能够拿到存储在store中的状态。

打开界面一看:

果然是拿到了状态state,那么我们就着手依次进行计算操作了~

首先是,点击“加”按钮,触发this.increment方法,我们在方法内部只需要调用redux的api:dispatch()即可。

// 加法
  increment = () => {
    const { value } = this.selectedNumber
    store.dispatch({ type: 'increment', data: parseInt(value) })
  }

我们通过调用dispatch()方法,传入一个我们自己定义的action动作对象,进行“加”的动作,这个动作对象会由store传给reducer函数,函数内部判断type值为‘increment’,那么就会对这个‘和’进行“加”的动作

代码实现完了,我们看一下效果:

完犊子,这怎么没加上去呢?

我们在count_reducer中console一下,找找原因,

打开控制台,我们发现状态是在跟随我们的点击随之改变的,但是页面并没有同步这个状态的改变。换句话说,也就是状态改变了,页面并没有随着状态的改变而重新渲染。在之前的纯react版本中,我们是通过this.setState()进行状态更新的,我们都知道react中,setState可以驱动视图更新,但是我们现在并不在组件的state中管理状态,而是通过redux进行状态的管理,所以才会导致页面没有更新。

我们需要手动监测redux中store状态,当状态发生变化时,我们自己去手动调用render,就可以解决页面不更新的问题。

componentDidMount() {
  // 监测redux中状态的变化,只要变化,就调用render
  store.subscribe(() => {
    this.setState({})
  })
}

我们可以使用这个api:store.subscribe()这个函数的参数接受一个回调函数,只要redux中的任何状态发生了改变,都会调用这个回调函数。我们可以在这个回调函数内部手动调用this.setState({}),只要这么调用了,就会自动重新render,这样就可以实现页面的更新了。

Count组件源码:

import React, { Component } from 'react'
//  引入store,用于获取redux中保存的状态
import store from '../../redux/store'

export default class Count extends Component {

  componentDidMount() {
    // 监测redux中状态的变化,只要变化,就调用render
    store.subscribe(() => {
      this.setState({})
    })
  }

  // 加法
  increment = () => {
    const { value } = this.selectedNumber
    store.dispatch({ type: 'increment', data: value * 1 })
  }
  // 减法
  decrement = () => {
    const { value } = this.selectedNumber
    store.dispatch({ type: 'decrement', data: value * 1 })
  }
  // 和为奇数时,加
  incrementIfOdd = () => {
    const { value } = this.selectedNumber
    const count = store.getState()
    // 除以2取余数,余数为0代表和为偶数,不加;余数不为0,代表和为奇数,加
    if (count % 2 !== 0) {
      store.dispatch({ type: 'increment', data: value * 1 })
    }
  }
  // 异步加
  incrementAsync = () => {
    const { value } = this.selectedNumber
    setTimeout(() => {
      store.dispatch({ type: 'increment', data: value * 1 })
    }, 500)
  }

  render() {
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={currentNode => { this.selectedNumber = currentNode }}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;&nbsp;&nbsp;
        <button onClick={this.increment}>加</button>&nbsp;
        <button onClick={this.decrement}>减</button>&nbsp;
        <button onClick={this.incrementIfOdd}>和为奇数时,加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    )
  }
}

redux精简版总结:

去除Count组件自身状态state;

src下建立:

/src/redux/store.js

/src/reudx/count_reducer.js

store.js

引入redux中的legacy_createStore函数,创建一个store;

legacy_createStore调用时要传入一个为其服务的reducer;

默认暴露store对象

count_reducer.js

reducer本质是一个函数,接收:preState,action作为参数,返回加工后的状态;

reducer有两个作用:初始化状态,加工状态;

reducer被第一次调用,是store自动触发的,传递的preState是undefined

状态改变后手动调用setState()以达到页面实时渲染的效果。

求和案例——redux完整版

在精简版中,我们使用的action对象是我们自己手动传入的action动作对象,但是redux有更好的api,来帮助我们自动创建action动作对象,不需要手动输入。我们来看看redux完整版是怎么实现集中状态管理的吧~

基于精简版,我们还需要在redux文件夹下创建一个文件

/src/redux/count_action_creator.js,该文件专门用于为Count组件生成action动作对象。

count_action_creator.js

/*
  该文件专门为Count组件生成action动作对象
*/
export const createIncrementAction = data => ({ type: 'increment', data })
// ({ type: 'increment', data }) 相当于 { return { type: 'increment', data } }

export const createDecrementAction = data => ({ type: 'decrement', data })

在Count组件中引入文件以及暴露的函数:

// 引入actionCreator,专门用于创建action对象
import {
  createIncrementAction,
  createDecrementAction
} from '../../redux/count_action_creator'

在所有需要使用dispatch分派action动作对象时,我们不再使用之前手打的action对象了,而是直接调用函数,自动生成action对象。

例如,在点击“加”按钮方法中:

// 加法
increment = () => {
  const { value } = this.selectedNumber
  store.dispatch(createIncrementAction(value * 1))
}

以此类推,将所有方法内手打的action对象更改为使用函数自动生成的action对象。

Count组件源码:

import React, { Component } from 'react'
//  引入store,用于获取redux中保存的状态
import store from '../../redux/store'
// 引入actionCreator,专门用于创建action对象
import {
  createIncrementAction,
  createDecrementAction
} from '../../redux/count_action_creator'

export default class Count extends Component {

  componentDidMount() {
    // 监测redux中状态的变化,只要变化,就调用render
    store.subscribe(() => {
      this.setState({})
    })
  }

  // 加法
  increment = () => {
    const { value } = this.selectedNumber
    store.dispatch(createIncrementAction(value * 1))
  }
  // 减法
  decrement = () => {
    const { value } = this.selectedNumber
    store.dispatch(createDecrementAction(value * 1))
  }
  // 和为奇数时,加
  incrementIfOdd = () => {
    const { value } = this.selectedNumber
    const count = store.getState()
    // 除以2取余数,余数为0代表和为偶数,不加;余数不为0,代表和为奇数,加
    if (count % 2 !== 0) {
      store.dispatch(createIncrementAction(value * 1))
    }
  }
  // 异步加
  incrementAsync = () => {
    const { value } = this.selectedNumber
    setTimeout(() => {
      store.dispatch(createIncrementAction(value * 1))
    }, 500)
  }

  render() {
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={currentNode => { this.selectedNumber = currentNode }}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;&nbsp;&nbsp;
        <button onClick={this.increment}>加</button>&nbsp;
        <button onClick={this.decrement}>减</button>&nbsp;
        <button onClick={this.incrementIfOdd}>和为奇数时,加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    )
  }
}

这样一来,基本的redux完整版就已经写完了;但是还有一个可优化的空间,我们在各个文件中都写了’increment’和’decrement’字符串,这会带来一些问题:

  • 字符串在IDE中没有变量提示,容易拼写错误;
  • 不便于维护和管理,一旦要更换字符串名称,需要到各个文件中都去更改一次; redux完整版(优化)

于是我们可以再创建一个js文件,用于常量的管理(常量维护):

/src/redux/constant.js

/*
  该模块是用于定义action对象中type类型的常量模块
  便于管理的同时避免程序员单词拼写出错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

更改,count_action_creator.js

import { INCREMENT, DECREMENT } from './constant'
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })

更改,count_reducer.js

+ import { INCREMENT, DECREMENT } from './constant'
const initState = 0
export default function countReducer(prevState = initState, action) {
  console.log(prevState, action)
  const { type, data } = action
  switch (type) {
  - case 'increment':
  + case INCREMENT:
      return prevState + data;
  - case 'decrement':
  + case DECREMENT:
      return prevState - data;
    default:
      return prevState
  }
}

如此一来,有利于后续项目越来越大的时候,进行变量名的管理与维护;而且,书写字符串是没有提示的,但是向这样引入变量,书写变量是有提示的,变量书写错误的话,IDE还有相应的错误提示,更加规范。

redux完整版总结:

  • count_action.js 专门用于创建action动作对象;
  • constant.js 放置由于编码疏忽可能写错的action中的type。

到此这篇关于Redux的工作流程的文章就介绍到这了,更多相关Redux工作流程内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • redux持久化之redux-persist结合immutable使用问题

    目录 前言 redux-persist 安装 使用到项目上 store.js index.js persist_reducer.js immutable 安装 使用到项目上 count_reducer.js 函数组件 类组件 结合使用存在的问题 组件 persist-reducer.js 解决 组件 persist-reducer.js 解决思路 前言 最近学习了redux以及react-redux的结合使用确实让redux在react中更好的输出代码啦~ 但是考虑到项目的各种需求,我们还是需要

  • React Redux使用配置详解

    目录 前言 redux三大原则 redux执行流程 redux具体使用 执行流程 redux使用流程 前言 在使用redux之前,首先了解一下redux到底是什么? 用过vue的肯定知道vuex,vuex是vue中全局状态管理工具,主要是用于解决各个组件和页面之间数据共享问题,对数据采用集中式管理,而且可以通过插件实现数据持久化 redux跟vuex类似,最主要的就是用作状态的管理,redux用一个单独的常量状态state来保存整个应用的状态,可以把它想象成数据库,用来保存项目应用中的公共数据

  • react-redux多个组件数据共享的方法

    目录 多个组件数据共享 总结: 多个组件数据共享 我们之前讲解的一直都是只有一个组件需要向redux读取状态,也就是Count这个求和组件.那么我们在实际使用redux的场景中,当然是有很多组件一起共享数据才需要使用到redux进行状态管理啦,现在我们就来看看多个组件通过redux实现数据共享的场景吧- 现在我们创建一个Person组件,同样的,Person组件的数据也交给redux管理.此时,Count组件也可以从redux中读取到Person组件的数据,Person组件也可以从redux中读

  • react-redux的基本使用

    目录 react-redux和redux的关系? react-redux基本使用 1.安装 2.配置 3.组件中使用 react-redux和redux的关系? redux是react中进行状态管理的js库(不是react插件),一般是管理多个组件中共享数据状态,和vue中的vuex是一样的 react-redux是redux官方react绑定库,能够使react组件从redux store中读取数据,并且向store分发actions以此来更新数据,说白了就是用于连接redux,它提供了con

  • redux工作原理讲解及使用方法

    目录 1. redux 是什么? 2.redux的原理 3. 如何使用 redux? (1).安装redux,创建redux文件夹,建立store.js (2).建立reducers.js (3).引入store.subscribe (4). 引入react-redux 1. redux 是什么? React 只是 DOM 的一个抽象层,并不是 Web 应用的完整解决方案.react只是一个轻量级的视图层框架,如果要做大型应用就要搭配视图层框架redux一起使用.主要引用于多交互.多数据源的场景

  • react-redux集中式状态管理及基本使用与优化

    目录 1.react-redux 2.连接容器组件与UI组件 3.react-redux基本使用 优化1.简写mapState和mapDispatch两个映射方法 优化2.Provider组件的使用 优化3.整合UI组件与容器组件 优化总结 1.react-redux react-redux把组件分为两类,一类叫做UI组件,一类叫做容器组件: UI组件的外侧都要包裹一个容器组件. 注意️:本文使用的示例为之前的文章中的示例.在之前的文章中已经做过了介绍,在本文就不再阐述. 建议阅读顺序是:[re

  • 详解Redux的工作流程

    目录 Redux理解 redux是什么 什么情况下需要使用redux? redux工作流程图 action reducer store 求和案例——纯react版 求和案例——redux精简版 求和案例——redux完整版 (当然不是这张图) Redux理解 redux是什么 redux是一个专门用于做状态管理的JS库(不是react插件库): 它可以在react.angular.vue等项目中,但基本与react配合使用: 作用:集中式管理react应用中多个组件共享的状态. 什么情况下需要使

  • 详解kubelet 创建pod流程代码图解及日志说明

    目录 正文 kubernetes调度pod简介 kubelet 创建pod代码及图解说明 kubelet 简介 kubelet创建及启动pod流程 kubelet 创建pod代码调用图解 kubelet 创建pod详细说明 kubelet 调用cri说明 kubelet创建pod整体架构图 kubelet创建pod日志说明 正文 本文将从如下方面介绍kubelet创建pod的过程 kubernetes调度pod简介 kubelet 创建pod代码图解说明 (本文重点) kubelet 调用cri

  • 详解http访问解析流程原理

    详解http访问解析流程原理 http访问网址域名解析流程: 1.在浏览器中输入www.qq.com域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析. 2.如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析. 3.如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器

  • 详解Spring 拦截器流程及多个拦截器的执行顺序

    拦截器是 Spring MVC 中的组件,它可以在进入请求方法前做一些操作,也可以在请求方法后和渲染视图后做一些事情. 拦截器的定义 SpringMVC 的拦截器只需要实现 HandlerInterceptor 接口,并进行配置即可.HandlerInterceptor 接口的定义如下: public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletRe

  • 详解Java中的流程控制

    1.分支结构的概念 当需要进行条件判断并做出选择时,使用分支结构 2.if分支结构 格式: if(条件表达式){ 语句块; } package com.lagou.Day04; import java.util.Scanner; /** * 编程使用if分支结构模拟网吧上网的过程 */ public class Demo01 { public static void main(String[] args) { //1.提示用户输入年龄信息并使用变量记录 System.out.println("请

  • Java详解聊天窗口的创建流程

    目录 Swing组件 JPanel JScrollPane JScrollPane的常用构造方法 JScrollPane的方法 如何向容器中添加按钮 文本组件 文本组件的常用方法 文本框(JTextField) 文本域(JTextArea) 聊天窗口示例 小结 Swing组件 JPanel JPanel和AWT中的Panel组件使用方法基本一致,是一个无边框,不能被移动,放大,缩小,或者关闭面板,它的默认布局管理器是FlowLayout,也可以用JPanel带参数的构造函数JPanel(Layo

  • Spring详解使用注解开发流程

    目录 在Spring4之后 要使用注解开发 必须保证aop包导入了 使用注解需要导入context约束 增加 注解的支持 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

  • 一文详解C++的程序流程控制

    目录 一.前言 2.三目运算符 3.switch语句 三.循环结构 1.while循环语句 2.do…while循环语句 3.for循环语句 四.跳转语句 1.break语句 2.continue语句 3.goto语句 一.前言 C++支持最基本的三种程序运行结构: 顺序结构 选择结构 循环结构 顺序结构:程序按顺序执行,不发生跳转 选择结构:依据条件是否满足,有选择的执行相应功能 循环结构:依据条件是否满足,循环多次执行某段代码 二.选择结构 1.if语句 作用: 执行满足条件的语句 if语句

  • 详解Android.activity销毁流程的工作原理

    继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是: onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a) 而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢? 这里我们我做一个简单的demo,让一个Activity a启动A

  • 详解linux SSH登录流程

    本文给大家详细介绍了ssh 密钥登录远程服务器流程和注意事项,以下是详细内容: 密钥登录比密码登录安全,主要是因为他使用了非对称加密,登录过程中需要用到密钥对.整个登录流程如下: 远程服务器持有公钥,当有用户进行登录,服务器就会随机生成一串字符串,然后发送给正在进行登录的用户. 用户收到远程服务器发来的字符串,使用与远程服务器公钥配对的私钥对字符串进行加密,再发送给远程服务器. 服务器使用公钥对用户发来的加密字符串进行解密,得到的解密字符串如果与第一步中发送给客户端的随机字符串一样,那么判断为登

随机推荐