Redux原理分析

2023-11-07

Redux原理分析

Redux是什么

很多人认为redux必须要结合React使用,其实并不是的,Redux 是 JavaScript 状态容器,只要你的项目中使用到了状态,并且状态十分复杂,那么你就可以使用Redux管理你的项目状态,它可以使用在react中,也可以使用中在Vue中,当然也适用其他的框架。

一.redux的工作原理

先上图(图片源于网络)
redux

  1. 首先我们找到最上面的state
  2. 在react中state决定了视图(ui),state的变化就会调用React的render()方法,从而改变视图
  3. 用户通过一些事件(如点击按钮,移动鼠标)就会像reducer派发一个action
  4. reducer接收到action后就会去更新state
  5. store是包含了所有了state,可以把他看做所有状态的集合

当然,现在可能看不懂这在瞎说啥,但是等把这篇文章看完再来这个图,和这段话,就会有恍然大明白的感觉

1.action

action本质上就是一个对象,它一定有一个名为type的key 如{type: 'add'},{type: 'add'}就是一个action
但是我们只实际工作中并不是直接用action ,而是使用action创建函数,(千万别弄混淆),
顾名思义action创建函数就是一个函数,它的作用就是返回一个action,如:

function add() {
    return {type: 'add'}
}

2.reducer

reducer其实就是一个函数,它接收两个参数,第一个参数是需要管理的状态state,第二个是action。reducer会根据传入的action的type值对state进行不同的操作,然后返回一个新的state,而不是在原有state的基础上进行修改,但是如果遇到了未知的(不匹配的)action,就会返回原有的state,不进行任何改变

function reducer(state = {money: 0}, action) {
    //返回一个新的state可以使用es6提供的Object.assign()方法,或扩展运算符(此方法需要babel-preset-state-3支持)
    switch (action.type) {
        case '+':
            return Object.assign({}, state, {money: state.money + 1});
        case '-':
            return {...state, ...{money: state.money - 1}};
        default:
            return state;
    }
}

3.store

你可以把store想成一个状态树,它包含了整个redeux应用的所有状态。
我们使用redux提供的createStore方法生成store

import {createStore} from 'redux';
const store = createStore(reducer);

store提供了几个方法供我们使用,下面是我们常用的3个:

store.getState();//获取整个状态树
store.dispatch();//改变状态,改变state的唯一方法
store.subscribe();//订阅一个函数,每当state改变时,都会去调用这个函数

接下来演示一个redux的完整应用,并且说明这三个方法该怎么用

import {createStore} from 'redux';

//给初始状态一个默认值:{money: 0}
function reducer(state = {money: 0}, action) {
    //返回一个新的state可以使用es6提供的Object.assign()方法,或扩展运算符(此方法需要babel-preset-state-3支持)
    switch (action.type) {
        case '+':
            return Object.assign({}, state, {money: state.money + 1});
        case '-':
            return {...state, ...{money: state.money - 1}};
        default:
            return state;
    }
}

//action创建函数,返回了一个action
function add() {
    return {type: '+'}
}

function subtraction() {
    return {type: '-'}
}

//创建单一状态树
const store = createStore(reducer);

console.log(store.getState());//{money: 0},初始的状态,没有任何改变(通过getState来获取目前的状态)

//store通过dispatch这个方法,并且传入action作为参数,对store进行了改变
store.dispatch(add());
console.log(store.getState());//{money: 1},reducer接受到了 '+' 这个命令,就捡到了一块钱

store.dispatch(subtraction());
console.log(store.getState());//{money: 0},reducer接受到了 '-' 这个命令,又掉了一块钱

store.dispatch({type:'我是来捣乱的'});
console.log(store.getState());//{money: 0},reducer接受到了一个不识别命令,返回原有的state

这个时候我们就会发现几个问题:

  1. 每次状态改变的时候我们都要console.log()才能知道改变后的状态,
  2. action的type实际上就是一个字符串,如果我们需要进行项目维护,更改type的值,就需要在多处进行修改,变得十分麻烦。

这个时候我们就可以使用store.subscribe()来订阅一个事件,代替我们在每次dispatch后都要console.log()后才能知道改变后的状态

function listen() {
    console.log(store.getState());
}

store.subscribe(listen);

将type维护成常量,这样我们在日后的维护过程中只需要对常量进行维护就可以了,我们目前这个demo使用到type的地方太少可能感觉不到,可是在实际项目中这个方法却非常的实用

const ADD = '+', SUBTRACTION = '-';

我们优化后的代码如下:

import {createStore} from 'redux';

//定义常量方便维护
const ADD = '+', SUBTRACTION = '-';

//给初始状态一个默认值:{money: 0}
function reducer(state = {money: 0}, action) {
    //返回一个新的state可以使用es6提供的Object.assign()方法,或扩展运算符(此方法需要babel-preset-state-3支持)
    switch (action.type) {
        case ADD:
            return Object.assign({}, state, {money: state.money + 1});
        case SUBTRACTION:
            return {...state, ...{money: state.money - 1}};
        default:
            return state;
    }
}

//action创建函数,返回了一个action
function add() {
    return {type: ADD}
}

function subtraction() {
    return {type: SUBTRACTION}
}

//打印改变后的状态
function listen() {
    console.log(store.getState());
}

//创建单一状态树
const store = createStore(reducer);

//订阅listen,每次dispatch后都会执行listen,从而打印状态(只有在执行dispatch后才会执行,状态初始化的时候并不会执行)
store.subscribe(listen);

console.log(store.getState());//初始的状态,没有任何改变

//store通过dispatch这个方法,并且传入action作为参数,对store进行了改变
store.dispatch(add());
store.dispatch(subtraction());
store.dispatch({type: '我是来捣乱的'});

/*控制台的打印结果如下:
{money: 0}
{money: 1}
{money: 0}
{money: 0}*/

补充:
一个应用只能有一个store,这个时候就会有一个问题 ,如果有多个reducer分别来处理不同的状态,而createStore是能接受一个reducer,这个时候我们就需要redux提供的combineReducers方法来将多个reducer结合成一个reducer

import {combineReducers} from 'redux';

const reducerFamily=combineReducers({
    reduceSon,
    reduceDaughter,
    reducerFather,
    reducerMother
})
const store = createStore(reducerFamily);

二.在React中使用redux

如果会react,那么也一定知道creact-react-app这个官方脚手架工具,首先使用creact-react-app创建一个项目,然后删除src目录下所有文件,接下来就可以愉快的敲代码了。

在src下创建三个文件
index.js

import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
//引入我们的reducer和action创建函数
import {reducer, add, subtraction} from './index.redux'
import App from './App'

//创建store
const store = createStore(reducer);

//store.subscribe方法接受的参数是一个函数,
// 所以将ReactDOM.render方法写在一个函数内
function listen() {
    //将store,action创建函数分别以属性的方式传递给子组件App
    ReactDOM.render(<App store={store} add={add} subtraction={subtraction}/>,
        document.querySelector('#root'));
}

//因为刚进入页面没有dispatch操作改变store,
// 所以listen不会执行,我们需要手动调用一次
listen();

//重点,改变了store,页面就会重新渲染,
// 可以试试不写这行代码会是怎样的效果
store.subscribe(listen);

App.js

import React from 'react'

export default class App extends React.Component {
    render() {
        //从属性中获取store,action创建函数
        const {store, add, subtraction} = this.props;
        //获取state
        let state = store.getState();
        return <div>
            <h1>我有{state.money}元</h1>

            {/*通过store.dispatch方法改变store,从而页面也会改变*/}
            <button onClick={() => {store.dispatch(add())}}>
                捡了一块钱
            </button>

            <button onClick={() => {store.dispatch(subtraction())}}>
                掉了一块钱
            </button>
        </div>
    }
}

index.redux.js

//定义常量方便维护
const ADD = '+', SUBTRACTION = '-';

//给初始状态一个默认值:{money: 0}
export function reducer(state = {money: 0}, action) {
    //返回一个新的state可以使用es6提供的Object.assign()方法,或扩展运算符(此方法需要babel-preset-state-3支持)
    switch (action.type) {
        case ADD:
            return Object.assign({}, state, {money: state.money + 1});
        case SUBTRACTION:
            return {...state, ...{money: state.money - 1}};
        default:
            return state;
    }
}

//action创建函数,返回了一个action
export function add() {
    return {type: ADD}
}

export function subtraction() {
    return {type: SUBTRACTION}
}

效果图
效果图

这样我们就将redux和react结合了起来但是这样我们可能会觉得麻烦,因为我们要将store和action创建函数传给子组件,当我们的action比较多时,子组件比较多时,就需要将store和大量的action创建函数一层层的多次传递下去。这样就会十分麻烦,因此我们就可以使用react-redux这个库来帮助我们实现这个麻烦的过程

三.react-redux的使用

1.Provider

react-redux给我们提供了一个Provider组件,我们可以把这个组件写在最外层,这样被Provider包裹的所有组件都可以通过props来获取state,无论组个组件藏得多么深。
Provider组件只接受一个属性,那就是store

那么我们index.js的代码就变成下面这样了:

import React from 'react'
import ReactDOM from 'react-dom'
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import {reducer} from './index.redux'
import App from './App'

//创建store
const store = createStore(reducer);

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>,
    document.querySelector('#root'));

2.connect

当然,只有Provider组件是不够的,我们还需要connect来帮助我们获取state和action,没错,connect就是帮助我们获取state和action的

那么问题就来了,我们的组件可不是需要项目中所有的state和action,只需要其中的一部分就可以了,所以connect会接受两个参数,第一个参数它可以帮我们筛选state,第二个参数可以帮我们筛选action。
我们可以把这两个参数写成函数的形式,
参数1,

function mapStateToProps(state) {
    return {
        money: state.money
    }
}

参数2,

function actionCreators() {
    return {
        subtraction,
        add
    }
}

我们可以发现这两个函数都是返回了一个对象,第一个函数返回了我们需要的state,第二个函数返回了我们需要的action创建函数

那么app.js 的代码就变成这样了:

import React from 'react'
import {connect} from 'react-redux'
import {add, subtraction} from './index.redux'

class App extends React.Component {
    render() {
        //因为connect的原因,state和action我们已经可以从属性中获取了
        const {money, add, subtraction} = this.props;

        return <div>
            <h1>我有{money}元</h1>

            {/*这个时候不需要我们dispatch了*/}
            <button onClick={add}>
                捡了一块钱
            </button>

            <button onClick={subtraction}>
                掉了一块钱
            </button>
        </div>
    }
}

//connect所需要的参数
//函数返回的我们需要的状态,我们需要money,就从state中取出money
//假如我们还需要house,就增加一个house:state.house
function mapStateToProps(state) {
    return {
        money: state.money
    }
}

//connect需要的第二参数
//返回我们需要的action创建函数
function actionCreators() {
    return {
        subtraction,
        add
    }
}

//上面两个函数返回的都是对象

//通过connect将state和action创建函数当做属性传递给组件
export default App = connect(mapStateToProps, actionCreators())(App);

如果熟悉es6装饰器的语法那就更好了,可以使我们的代码变得更优雅
app.js

import React from 'react'
import {connect} from 'react-redux'
import {add, subtraction} from './index.redux'

@connect(
    state => ({money: state.money}),
    {
        subtraction,
        add
    })
export default class App extends React.Component {
    render() {
        //因为connect的原因,state和action我们已经可以从属性中获取了
        const {money, add, subtraction} = this.props;

        return <div>
            <h1>我有{money}元</h1>

            {/*这个时候不需要我们dispatch了*/}
            <button onClick={add}>
                捡了一块钱
            </button>

            <button onClick={subtraction}>
                掉了一块钱
            </button>
        </div>
    }
}

看到这里再回头看看最开始图片,就能搞清楚redux的工作流程究竟是怎样的。

原文https://segmentfault.com/a/1190000019849834?utm_source=tuicool&utm_medium=referral 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redux原理分析 的相关文章

随机推荐

  • 《Apache MINA 2.0 用户指南》第七章:事件处理器

    最近准备将Apache MINA 2 0 用户指南英文文档翻译给大家 但是我偶然一次百度 发现 Defonds 这位大牛已经翻译大部分文档 原文链接 http mina apache org mina project userguide c
  • [webpack问题]TypeError: __webpack_require__(...).context is not a function

    require context directory useSubdirectories regExp directory 表示检索的目录 useSubdirectories 表示是否检索子文件夹 regExp 匹配文件的正则表达式 一般是文
  • BeanCreationException异常,注入Bean异常

    org springframework beans factory BeanCreationException Error creating bean with name XXX 注入bean异常 出现这个异常就是找不到对应的JavaBea
  • mac改成类似微软键盘偏好设置

    以前我做过笔记 但是好像印象还不是很深刻 因为我自己还是忘记了 我又写了一篇 首先是蛋疼的切换输入法问题 中文输入法和英文输入法的问题真不习惯 切换输入法改正方法 进入系统偏好设置 键盘 快捷键 输入法 选择上一个输入法 勾选 发现右边 空
  • Java类和对象(重点详解)

    类和对象 类和对象的关系 类的介绍 类变量 静态变量 public private 一些建议和小结 写在最后的话 这段时间博主学习了一些Java中类和对象的知识 今天我们就来聊聊Java中的类和对象 类和对象的关系 类其实就是一个模板 比如
  • oracle重复数据保留需要的一条数据

    由于功能开发进度的问题 人员录入的时候仅能够多次录入 不能够录入之后直接以该数据未蓝本引入导致多部门的时候必须多次创建冗余的数据 且由于数据录入的不规范 录入了许多相同的数据 特别是同单位同部门的数据 故需要处理此类数据 因此需要对此类重复
  • Unity --- 文本输入框的使用

    文本输入框有两个版本 一个是旧版的文本输入框 一个是新版的输入字段 这里选择旧版 其实旧版和新版的唯一区别就是text组件有些不同 其它的没啥不同 上面这两张图就是文本输入框中最重要的 input field 输入区域 组件的参数了 上面这
  • leetcode报错:member access within null pointer of type 'struct ListNode'

    背景 在编写判断单链表是否有环时 出现这错误 错误出现原因 错误出现原因 color Red text 38169 35823 20986 29616 21407 22240 因为试图使用空指针 解决方法 解决方法 color Red te
  • 音频模块的介绍

    一 术语总结 1 HIFI 级 HIFI 一词通常指高保真音频 High Fidelity Audio 是指尽可能保持音频信号的原始质量 让听众感受到最真实的音乐表现 因此 HIFI级 通常指具有高保真音频性能的产品或设备 例如高保真耳机
  • MAC使用Visual Studio Code开发C/C++

    MAC使用Visual Studio Code开发C C 一 前置概念 理解 二 环境准备 三 编译 运行 四 补充 一 前置概念 理解 VS code只是一个纯文本编辑器 editor 不是IDE 集成开发环境 不含编译器 compile
  • html天气插件iframe,分享常用7款天气预报代码iframe嵌入网页方式

    如果在网站上加入天气预报功能 你找不到更好的天气预报代码 可以看下本站和大家分享的7款天气预报代码iframe嵌入网页方式 天气预报代码1 src http appnews qq com cgi bin news qq search cit
  • python:pydub模块

    一 安装 1 安装模块 pip install pydub 2 安装插件 云盘中下载文件ffmpeg 打开电脑上的控制面板 系统 高级系统设置 环境变量 然后双击path 看到如下的界面 然后点新建会出现一个新建的地址栏 你需要在这个新建地
  • 备忘:maven 错误信息: Plugin execution not covered by lifecycle configuration

    在一个pom文件中 由于需要设置了一下几个默认goal的版本号 如下
  • 算法题:回文数

    力扣 思路 用栈 public static boolean isPalindrome int x if x lt 0 return false if x 0 return true 怎么取每位数字 String s String valu
  • 2023-DataWorks数仓开发手册收藏版

    DataWorks开发规范 1 数仓基本概念 1 4 1 ods数据源层表命名规范 1 4 2 dim维表层表命名规范 1 4 3 dwd数据明细层表命名规范 1 4 3 dws数据明细层表命名规范 1 4 4 ads数据应用层表命名规范
  • Docker从入门到精通

    目录 一 初识 Docker 1 Docker概念 2 安装Docker CentOS系统 3 Docker的架构 4 阿里云镜像加速 5 Docker容器虚拟化 与 传统虚拟机比较 二 Docker 服务相关命令 1 启动docker 服
  • 普通树转二叉树:左儿子右兄弟表示法

    这两天在吃力地学DP的优化 被虐地不行不行的 搞个小插曲 左儿子右兄弟 顾名思义 是一棵转换后的树 它是一棵二叉树 一个节点的左子树表示的是原树中这个节点的子节点 一个节点的右子树表示的是这个节点在原树中的兄弟 父节点相同的点 这么表示有什
  • 【机器学习】Reinforcement Learning-强化学习学习笔记

    一 强化学习的定义 1 1 什么是强化学习 首先 强化学习并不是某一种特定的算法 而是一类算法的统称 解决序列决策问题的一类方法 通过寻求最优策略 获取最大回报 强化学习就是智能体从环境到动作映射的学习 以使回报信号 激励信号 函数值最大
  • 在LINUX上安装Memcache服务器(实践记录)

    下载并安装Memcache服务器端 服务器端主要是安装memcache服务器端 下载 http www danga com memcached dist memcached 1 2 2 tar gz memcached 1 4 5 tar
  • Redux原理分析

    Redux原理分析 Redux是什么 很多人认为redux必须要结合React使用 其实并不是的 Redux 是 JavaScript 状态容器 只要你的项目中使用到了状态 并且状态十分复杂 那么你就可以使用Redux管理你的项目状态 它可