Redux原理分析

Redux原理分析

Redux是什么javascript

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

一.redux的工做原理

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

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

固然,如今可能看不懂这在瞎说啥,可是等把这篇文章看完再来这个图,和这段话,就会有恍然大明白的感受react

1.action

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

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

2.reducer

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

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方法生成storebabel

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

store提供了几个方法供咱们使用,下面是咱们经常使用的3个:网络

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

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

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()后才能知道改变后的状态dom

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的工做流程到底是怎样的。

相关文章
相关标签/搜索