资讯专栏INFORMATION COLUMN

Redux原理分析

cooxer / 2590人阅读

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

Redux原理分析

Redux是什么

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

先上图(图片源于网络)

首先我们找到最上面的state

在react中state决定了视图(ui),state的变化就会调用React的render()方法,从而改变视图

用户通过一些事件(如点击按钮,移动鼠标)就会像reducer派发一个action

reducer接收到action后就会去更新state

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

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

每次状态改变的时候我们都要console.log()才能知道改变后的状态,

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(,
        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 

我有{state.money}元

{/*通过store.dispatch方法改变store,从而页面也会改变*/}
} }

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(
    
        
    ,
    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 

我有{money}元

{/*这个时候不需要我们dispatch了*/}
} } //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 

我有{money}元

{/*这个时候不需要我们dispatch了*/}
} }

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105959.html

相关文章

  • Redux原理分析

    摘要:调用链中最后一个会接受真实的的方法作为参数,并借此结束调用链。总结我们常用的一般是除了和之外的方法,那个理解明白了,对于以后出现的问题会有很大帮助,本文只是针对最基础的进行解析,之后有机会继续解析对他的封装 前言 虽然一直使用redux+react-redux,但是并没有真正去讲redux最基础的部分理解透彻,我觉得理解明白redux会对react-redux有一个透彻的理解。 其实,...

    sumory 评论0 收藏0
  • redux原理分析

    摘要:介绍是一个针对应用的可预测的状态管理器。中的设计模式装饰者模式定义装饰者模式用于给对象动态地增加职责。连接操作不会改变原来的组件类,而是返回一个新的已与连接的组件类。的这行代码表示它对的数据进行订阅。 redux介绍 redux是一个针对JavaScript应用的可预测的状态管理器。 redux中的设计模式 装饰者模式 定义:装饰者模式用于给对象动态地增加职责。 我们来看看redux最...

    jubincn 评论0 收藏0
  • 【源码解析】redux-thunk

    摘要:的返回值是函数,这个函数经调用,传入参数,之后会在中间件链上进行传递,只要保证每个中间件的参数是并且将传递给下一个中间件。 了解了Redux原理之后,我很好奇Redux中间件是怎么运作的,于是选了最常用的redux-thunk进行源码分析。 此次分析用的redux-thunk源码版本是2.2.0,redux源码版本是3.7.2。并且需要了解Redux原理 redux中间件都是由redu...

    simpleapples 评论0 收藏0
  • redux源码分析

    摘要:背景如今在如此复杂的前端交互和逻辑中,如果是单靠框架,,是远远不够的,还需要一个对内部的数据状态进行管理的机制才行,而对于这种数据管理机制如今较为热门是主要有几个,,这次主要讲述的就是对源码进行分析。 由于这段时间一直很忙,所以本想六月初研究完redux源码就写一篇博客记录一下自己的心得,但一直现在才空闲出来,废话不多说了,直接说主题吧。 背景:如今在如此复杂的前端交互和逻辑中,如果是...

    Sike 评论0 收藏0
  • 基于react+react-router+redux+socket.io+koa开发一个聊天室

    摘要:最近练手开发了一个项目,是一个聊天室应用。由于我们的项目是一个单页面应用,因此只需要统一打包出一个和一个。而就是基于实现的一套基于事件订阅与发布的通信库。比如说,某一个端口了,而如果端口订阅了,那么在端,对应的回调函数就会被执行。 最近练手开发了一个项目,是一个聊天室应用。项目虽不大,但是使用到了react, react-router, redux, socket.io,后端开发使用了...

    NusterCache 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<