Flux --> Redux --> Redux React 基础实例教程

本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用javascript

 

假设你会一些ES6、会一些React、有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识php

通常来讲,推荐使用 ES6+React+Webpack 的开发模式,但Webpack须要配置一些东西,你能够先略过,本文不须要Webpack基础html

入门,只是一些基础概念和用法的整理,更完整的内容推荐去看看文档,英文中文java

(不过我我的认为,官方文档的例子相对来讲太复杂了,很难让新手立刻抓住重点)react

(官方的例子正统且联系业务,不一样类型的操做或数据放在不一样文件中,很规范,但也很绕,因此本文使用的例子很是简单,且直接放在一个文件中 以便于理解)git

 

搭飞机前往:github

Flux思想Redux基本概念Redux的使用Redux在React中的使用(同步)Redux在React中的使用(异步,使用中间件)ajax

 

1、Flux

Flux是一种概念思想,或者说是一种应用架构npm

根据它的概念,一个应用中的数据流动应是单向的,且应用中的全部数据保存在一个位置,数据变化时保证视图也同步变化,保证了数据和视图的状态是一一对应起来的json

此应用应该分为四层:

  • view层:应用的视图,页面的(数据)展现
  • action层:(视图)发出的某些动做,好比点击事件
  • dispatcher层:派发器,接收action并处理这些动做,更新数据
  • store层:存放应用的数据,数据更新后,提醒view层更新视图

它的概念思想可能一时半会理解不了,不要紧,过段时间就行了

 

2、Redux

上面说到,Flux只是一个思想,咱们能够根据这个思想来本身实现出一个技术方案,来解决问题

是要解决什么问题呢?

在使用React的过程当中,在组件间通讯的处理上咱们用了回调的方式,若是组件层级很深,不一样组件间的数据交流就会致使回调及其触发的函数很是多,代码冗杂

须要一个状态管理方案,方便管理不一样组件间的数据,及时地更新数据

而Flux思想中的Store层,切合了这个问题

 

1. 什么是Redux

Redux是受Flux启发实现的一个技术方案,能够认为它是Flux的产物,但它并无沿用Flux全部的思想

主要区别是Flux的派发器dispatcher,Redux认为使用派发器就得增长事件订阅/发布的规则,倒不如直接用函数调用的方式来得实在,简单而统一,因此就将处理action的任务交给了store层(直接调用这个对象的dispatch方法)

2. 何时用Redux

Redux说简单简单,由于也就几个API,理解好概念就好用了;说复杂也复杂,由于它将一个应用分红了不一样部分(action、处理action、store数据等),在正规的项目中是推荐将各部分区分到不一样文件中的(如官方的例子),文件数量不少可能会比较难管理,固然,细粒化了也就减小了耦合度。最后还要加个操做把Redux的数据更新给React组件(若是用了React)

在大多数状况下,Redux是不须要用的,如UI层很是简单,没有太多互动的

  • 用户的使用方式很是简单
  • 用户之间没有协做
  • 不须要与服务器大量交互,也没有使用 WebSocket
  • 视图层(View)只从单一来源获取数据

 

而在多交互,多数据源的时候能够考虑使用

  • 用户的使用方式复杂
  • 不一样身份的用户有不一样的使用方式(好比普通用户和管理员)
  • 多个用户之间能够协做与服务器大量交互,或者使用了WebSocketView
  • 要从多个来源获取数据

在须要管理复杂组件状态的时候,能够考虑使用

  • 某个组件的状态,须要共享某个状态
  • 须要在任何地方均可以拿到一个组件
  • 须要改变全局状态一个组件
  • 须要改变另外一个组件的状态

 

3. 开始用Redux

上面讲了那么多字,仍是看代码来得实在

这里先纯粹讲Redux,毕竟它和React是没啥关系的

首先是环境配置,基本上都会使用ES6,因此Babel的支持是必须的

而后是Redux的支持,若是使用Webpack打包编译,就用npm安装个redux包

这里采用直接在浏览器引入的方式,使用 这个库

  <body>
        <div id="box"></div>

        <script type="text/javascript" src="../lib/react.js"></script>
        <script type="text/javascript" src="../lib/react-dom.js"></script>
        <script type="text/javascript" src="../lib/redux.min.js"></script>
        <script type="text/javascript" src="../build/reduxStart.js"></script>
    </body>

最后build里的为demo代码用babel编译以后的es5文件

在全局之中有Redux这个对象,取其中的几个属性来用

let {Component} = React;
let {render} = ReactDOM;
let {createStore, combineReducers} = Redux;

3.1 Redux须要一个store来存放数据

这个store就由createStore建立

3.2 须要定义各个操做是什么,即action

一般来讲它是一个对象,包含type属性表示是什么操做,以及其余属性携带一些数据

它可能长这样子,建议是遵循官方的 一些规范

let upAction = {
    type: 'UP'
};

咱们不止会传type,还会传一些值,若是传不一样的值就let一次就太冗杂了,通常来讲就会用一个方法代替

let upAction = function(value) {
    return {
        type: 'up',
        value
    };
};

3.3 须要定义怎么处理操做,在redux中它被称做reducer

为何把这种操做称做reducer呢

redux引入了JS数组reduce方法的思想,JS的reduce长这样

var arr = [1, 2, 3, 4];

var num = arr.reduce((a, b) => {
    return a + b;
});

num // 10

var num = arr.reduce((a, b) => {
    return a + b;
}, 5);

num // 15

固然了,只是看起来像,实际上差异挺大的,redux的reducer看起来像这样

let upReducer = function(state = 0, action) {
    switch (action.type) {
        case 'up':
            return state + action.value;
        default:
            return state;
    }
};

它是一个函数,接收两个参数,第一个参数为数据(即某个状态state),第二个参数为action操做对象

为了切合store中数据与view中视图是一一对应的,reducer规定需始终返回新的state数据,不能直接在原有state中修改

而且,建议在匹配不到action的时候始终返回默认的state状态,且建议在第一个参数中初始化默认的state值

 

3.4 在建立store的时候绑定reducer

redux基本上把全部操做都给了store,因此大部分方法都是用store来调用的

其实,你也能够认为Flux中的派发器(dispatcher)就是在里面自动绑定的

let store = createStore(reducer);

// let store = createStore(reducer, 10);

如上,建立store的时候传入reducer,能够接收第二个参数表示reducer使用的默认值

3.5 视图发出action动做

在某个时刻,发出了这些动做

store.dispatch(upAction(10));
store.dispatch(upAction(100));

3.6 使用store.getState()获取store中的数据

3.7 动做发出后,reducer匹配动做更新store中的数据,视图view层使用subscribe监听数据的改变

store.subscribe(() => console.log(store.getState()));

 

来看一下完整的代码

let {Component} = React;
let {render} = ReactDOM;
let {createStore, combineReducers} = Redux;

let upAction = function(value) {
    return {
        type: 'up',
        value
    };
}

let upReducer = function(state = 0, action) {
    switch (action.type) {
        case 'up':
            return state + action.value;
        default:
            return state;
    }
};

let store = createStore(upReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

console.log(store.getState());

store.subscribe(() => console.log(store.getState()));

store.dispatch(upAction(10));
store.dispatch(upAction(100));

注意上面createStore中第二个参数是用于Redux DevTool的配置,即这个东西

使用这个工具能够便于开发

看看上面代码的输出

初始获取到的值为0,两次action后分别更新相关的数据状态。若是加上初始默认值10

let store = createStore(upReducer, 10, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

 

3.8 使用多个reducer时,使用Redux的combineReducers方法

action固然不会只是up,多是down,这时能够直接用switch语句切换;但若是action不是这里增减的操做,放在一块儿就有点乱套了

因此须要定义多个reducer,但createStore方法只接收一个reducer,因此就须要整合多个reducer为一个,再统一传入

它看起来像这样

let reducer = combineReducers({upReducer, downReducer});
// let reducer = combineReducers({
//     upReducer: upReducer, 
//     downReducer: downReducer
// });

接收一个reducer组成的对象,属性表示该reducer对应的state名(如state.upReducer),值表示这个reducer

固然,这个方法咱们能够本身定义,看起来是这样

let myCombineReducers = function(reducerObj) {
    let newState = {};

    return function(state = {}, action) {
        for (let item in reducerObj) {
            newState[item] = reducerObj[item](state[item], action);
        }

        return newState;
    }
};

其实就是遍历reducer组,返回一个统一的新的reducer,且新的reducer中返回一个新的state

看看Redux中的实现,完整多了

 1 function combineReducers(reducers) {
 2       var reducerKeys = Object.keys(reducers);
 3       var finalReducers = {};
 4       for (var i = 0; i < reducerKeys.length; i++) {
 5         var key = reducerKeys[i];
 6 
 7         if (true) {
 8           if (typeof reducers[key] === 'undefined') {
 9             (0, _warning2['default'])('No reducer provided for key "' + key + '"');
10           }
11         }
12 
13         if (typeof reducers[key] === 'function') {
14           finalReducers[key] = reducers[key];
15         }
16       }
17       var finalReducerKeys = Object.keys(finalReducers);
18 
19       if (true) {
20         var unexpectedKeyCache = {};
21       }
22 
23       var sanityError;
24       try {
25         assertReducerSanity(finalReducers);
26       } catch (e) {
27         sanityError = e;
28       }
29 
30       return function combination() {
31         var state = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
32         var action = arguments[1];
33 
34         if (sanityError) {
35           throw sanityError;
36         }
37 
38         if (true) {
39           var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
40           if (warningMessage) {
41             (0, _warning2['default'])(warningMessage);
42           }
43         }
44 
45         var hasChanged = false;
46         var nextState = {};
47         for (var i = 0; i < finalReducerKeys.length; i++) {
48           var key = finalReducerKeys[i];
49           var reducer = finalReducers[key];
50           var previousStateForKey = state[key];
51           var nextStateForKey = reducer(previousStateForKey, action);
52           if (typeof nextStateForKey === 'undefined') {
53             var errorMessage = getUndefinedStateErrorMessage(key, action);
54             throw new Error(errorMessage);
55           }
56           nextState[key] = nextStateForKey;
57           hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
58         }
59         return hasChanged ? nextState : state;
60       };
61     }
View Code

 

加上个down操做,来看看完整代码

let {Component} = React;
let {render} = ReactDOM;
let {createStore, combineReducers} = Redux;

let upAction = function(value) {
    return {
        type: 'up',
        value
    };
}
let downAction = function(value) {
    return {
        type: 'down',
        value
    };
}

let upReducer = function(state = 0, action) {
    switch (action.type) {
        case 'up':
            return state + action.value;
        default:
            return state;
    }
};

let downReducer = function(state = 0, action) {
    switch (action.type) {
        case 'down':
            return state - action.value;
        default:
            return state;
    }
};

let reducer = combineReducers({upReducer, downReducer});

let store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

console.log(store.getState());

store.subscribe(() => console.log(store.getState()));

store.dispatch(upAction(10));
store.dispatch(upAction(100));
store.dispatch(downAction(10));
store.dispatch(downAction(100));

给reducer设个初始值,要注意的是,这个初始值是针对整个state的

若是只有一个reducer,那reducer函数中的state就是这个state

若是用combineReducer整理了多个reducer,那各个reducer函数中的state是整个state中的reducer同名属性的值

let reducer = combineReducers({upReducer, downReducer});

let store = createStore(
    reducer, 
    {upReducer: 10, downReducer: 10}, 
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

如上代码定义了初始值,看看执行结果

 

四. 在React中使用Redux

Redux是一个独立的技术方案,咱们将它运用到React项目中

接下来的问题主要有三个:

  • 如何将store中的数据同步给React组件
  • 如何让React组件调用Redux的dispatch方法
  • 上面两个

直接点,就是在React组件中调用Redux的subscribe方法来监听同步数据,再在某个时机调用dispatch便可

但官方并不建议使用subscribe这个方法,而是建议使用封装好的另外一个库 React-Redux

 

4.1 引入库

与引入Redux相似,你可使用Webpack引入包或浏览器直接引入这个库

而后在全局window下能够获取到这个对象,取一些用到的属性如

let {Provider, connect} = ReactRedux;

4.2 先定义一个有增加操做的React组件

class Increase extends Component {
    constructor(props) {
        super(props);
    }

    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
    }

    increase() {
        let {dispatch} = this.props;
        dispatch({
            type: 'up'
        });
    }

    render() {
        return <p onClick={this.increase.bind(this)}>increase: {this.props.number}</p>
    }
}

组件定义了一个action,即点一次执行一次增加(increase)函数,里面调用dispatch方法发出action,先看看其余东西

4.3 定义一个reducer

function couterUp(state = {number: 100}, action) {
    switch (action.type) {
        case 'up':
            return {
                number: state.number + 1
            };
        default:
            return state;
    }
}

4.4 建立一个store

let store = createStore(couterUp, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

4.4 使用ReactRedux的connect方法

要将Redux中的数据同步给React,须要用到这个方法

它看起来像是这样子

let APP = connect(
    mapStateToProps,
    mapDispatchToProps
)(Increase);

能够把它当作是一个中间件,首先接收几个参数完成配置阶段,而后传入React组件,包装成一个新的东东(它并无直接修改Increase组件)

而通常来讲,通常来讲会传入两个参数(支持四个参数),顾名思义:

第一个参数(类型为函数)

若是不传或置入undefinednull,则表示不须要进行数据更新;不然表示将store中的数据经过props的形式传给React组件

第二个参数(类型为函数)

若是不传或置入undefinednull,则表示将React-Redux中默认的dispatch方法传给React组件;不然表示将redux中的dispatch发出动做经过props的形式传给React组件

 

注意到上面的React组件代码中,经过props获取到了dispatch方法,而后自行发出动做

  increase() {
        let {dispatch} = this.props;
        dispatch({
            type: 'up'
        });
    }

若是要这样作,mapDispatchToProps 这里就不传入了,即

let APP = connect(
    mapStateToProps
)(Increase);

用回常见的方式,在React组件中改一改,直接从props中获取某个dispatch的发出动做

render() {
        return <p onClick={this.props.increase}>increase: {this.props.number}</p>
    }

同时修改两个都传入

let APP = connect(
    mapStateToProps,
    mapDispatchToProps
)(Increase);

4.5 mapStateToProps 和 mapDispatchToProps

咱们定义一下这两个参数(函数),它看起来长这样

function mapStateToProps(state) {
    return {
        number: state.number
    };
}

function mapDispatchToProps(dispatch) {
    return {
        increase: () => dispatch({
            type: 'up'
        })
    };
}

mapStateToProps 中第一个参数为一个对象,表示store中总体的state数据

固然,第一个参数也能够为函数,也能够接收第二个参数,表示自身拥有的属性(ownProps),具体能够看API

最后它返回了一个新的对象,表示要传给React组件的数据

与mapStateToProps相似,mapDispatchToProps 也能够接收两个参数,

第一个表示当前的dispatch方法,第二个表示自身拥有的属性(ownProps

最后它返回了一个action发出动做(一个函数),传给React组件调用

 

4.6 使用Provider

基本好了,只差一步:将connect包装组件后生成的新东东与实际页面联系起来

使用ReactRedux提供的<Provider />,它看起来是这样

render(
    <Provider store={store}>
        <APP />
    </Provider>,
    document.getElementById('box')
);

使用store属性传入上面的store对象

在children中置入有connect生成的APP组件,注意这里只能包含一个父层

若是向其中传入属性,如

<APP name="app" />

那么,mapStateToProps中的第二参数ownProps就能够拥有这个name属性

 

完整代码

let {Component} = React;
let {render} = ReactDOM;
let {createStore, combineReducers} = Redux;
let {Provider, connect} = ReactRedux;

class Increase extends Component {
    constructor(props) {
        super(props);
    }

    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
    }

    render() {
        return <p onClick={this.props.increase}>increase: {this.props.number}</p>
    }
}

function couterUp(state = {number: 100}, action) {
    switch (action.type) {
        case 'up':
            return {
                number: state.number + 1
            };
        default:
            return state;
    }
}

function mapStateToProps(state) {
    return {
        number: state.number
    };
}

function mapDispatchToProps(dispatch) {
    return {
        increase: () => dispatch({
            type: 'up'
        })
    };
}

let APP = connect(
    mapStateToProps,
    mapDispatchToProps
)(Increase);

let store = createStore(couterUp, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

render(
    <Provider store={store}>
        <APP />
    </Provider>,
    document.getElementById('box')
);
View Code

看一下运行结果

4.7 多个React组件中的使用

上面说的是单个React组件中的使用,实际使用中会有多个组件

多个组件的使用相似单个,只不过须要注意两点

  • <Provider />中只能包含一个父级
  • mapStateToProps中第一个参数是指总体store中的数据

下面以两个组件的栗子,看看如何实现

 

4.7.1 首先定义两个组件,一增一减

class Increase extends Component {
    constructor(props) {
        super(props);
    }

    componentWillReceiveProps(nextProps) {
        console.log('increase: ', nextProps);
    }

    render() {
        return <p onClick={this.props.increase}>increase: {this.props.number}</p>
    }
}

class Decrease extends Component {
    constructor(props) {
        super(props);
    }

    componentWillReceiveProps(nextProps) {
        console.log('decrease: ', nextProps);
    }

    render() {
        return <p onClick={this.props.decrease}>decrease: {this.props.number}</p>
    }
}

4.7.2 定义对应的两个reducer

function couterUp(state = {number: 100}, action) {
    switch (action.type) {
        case 'up':
            return {
                number: state.number + 1
            };
        default:
            return state;
    }
}

function counterDown(state = {number: -100}, action) {
    switch (action.type) {
        case 'down':
            return {
                number: state.number - 1
            };
        default:
            return state;
    }
}

4.7.3 建立store

let couter = combineReducers({
    couterUp,
    counterDown
});

let store = createStore(
    couter,
    {couterUp: {number: 10}},
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

4.7.4 建立链接两个组件对应的两个mapStateToProps 和 mapDispatchToProps

注意state为整个store中的state,取值要取各reducer同名属性如 state.couterUp

function mapStateToProps_1(state) {
    return {
        number: state.couterUp.number
    };
}

function mapDispatchToProps_1(dispatch) {
    return {
        increase: () => dispatch({
            type: 'up'
        })
    };
}

function mapStateToProps_2(state, props) {
    return {
        number: state.counterDown.number
    };
}

function mapDispatchToProps_2(dispatch) {
    return {
        decrease: () => dispatch({
            type: 'down'
        })
    };
}

4.7.5  各组件用connect包装

let APP_1 = connect(
    mapStateToProps_1,
    mapDispatchToProps_1
)(Increase);

let APP_2 = connect(
    mapStateToProps_2,
    mapDispatchToProps_2
)(Decrease);

4.7.6  置入<Provider />中

注意只能有一个父级,因此得先简单包装一层

let APP = () => (
    <div>
        <APP_1 />
        <APP_2 name="APP_2"/>
    </div>
);

render(
    <Provider store={store}>
        <APP />
    </Provider>,
    document.getElementById('box')
);

完整代码

  1 let {Component} = React;
  2 let {render} = ReactDOM;
  3 let {createStore, combineReducers} = Redux;
  4 let {Provider, connect} = ReactRedux;
  5 
  6 class Increase extends Component {
  7     constructor(props) {
  8         super(props);
  9     }
 10 
 11     componentWillReceiveProps(nextProps) {
 12         console.log('increase: ', nextProps);
 13     }
 14 
 15     render() {
 16         return <p onClick={this.props.increase}>increase: {this.props.number}</p>
 17     }
 18 }
 19 
 20 class Decrease extends Component {
 21     constructor(props) {
 22         super(props);
 23     }
 24 
 25     componentWillReceiveProps(nextProps) {
 26         console.log('decrease: ', nextProps);
 27     }
 28 
 29     render() {
 30         return <p onClick={this.props.decrease}>decrease: {this.props.number}</p>
 31     }
 32 }
 33 
 34 function couterUp(state = {number: 100}, action) {
 35     switch (action.type) {
 36         case 'up':
 37             return {
 38                 number: state.number + 1
 39             };
 40         default:
 41             return state;
 42     }
 43 }
 44 
 45 function counterDown(state = {number: -100}, action) {
 46     switch (action.type) {
 47         case 'down':
 48             return {
 49                 number: state.number - 1
 50             };
 51         default:
 52             return state;
 53     }
 54 }
 55 
 56 function mapStateToProps_1(state) {
 57     return {
 58         number: state.couterUp.number
 59     };
 60 }
 61 
 62 function mapDispatchToProps_1(dispatch) {
 63     return {
 64         increase: () => dispatch({
 65             type: 'up'
 66         })
 67     };
 68 }
 69 
 70 function mapStateToProps_2(state, props) {
 71     return {
 72         number: state.counterDown.number
 73     };
 74 }
 75 
 76 function mapDispatchToProps_2(dispatch) {
 77     return {
 78         decrease: () => dispatch({
 79             type: 'down'
 80         })
 81     };
 82 }
 83 
 84 let couter = combineReducers({
 85     couterUp,
 86     counterDown
 87 });
 88 
 89 let store = createStore(
 90     couter,
 91     {couterUp: {number: 10}},
 92     window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
 93 );
 94 
 95 
 96 let APP_1 = connect(
 97     mapStateToProps_1,
 98     mapDispatchToProps_1
 99 )(Increase);
100 
101 let APP_2 = connect(
102     mapStateToProps_2,
103     mapDispatchToProps_2
104 )(Decrease);
105 
106 let APP = () => (
107     <div>
108         <APP_1 />
109         <APP_2 name="APP_2"/>
110     </div>
111 );
112 
113 render(
114     <Provider store={store}>
115         <APP />
116     </Provider>,
117     document.getElementById('box')
118 );
View Code

 

Good ! 完成了,看看结果

 

4.7.7  再看connect方法剩余的两个参数

connect方法接收可接收四个参数,上面已经谈到了前两个,后两个不那么经常使用

第三个参数,这里很少说:[mergeProps(stateProps, dispatchProps, ownProps): props] (Function)

第四个参数[options(Object)

这个options中有以下几个属性:

  • pure: true(默认)|false 表示是否在调用connect前三个参数的函数方法以前先检测先后store中的值是否改变,改变才调用,不然不调用
  • areStatesEqual: 函数,当pure为true时调用这个函数检测是否相等,返回true|false表示是否相等,默认以严格相等===来判断先后值是否相等
  • areOwnPropsEqual: 相似areStatesEqual,只不过它默认是用不严格相等==来判断
  • areStatePropsEqual: 相似areStatesEqual,只不过它默认是用不严格相等==来判断
  • areMergedPropsEqual: 相似areStatesEqual,只不过它默认是用不严格相等==来判断

来看个例子,如今要手动的定义这个参数

针对Decrease,在减1时直接返回了false

let APP_2 = connect(
    mapStateToProps_2,
    mapDispatchToProps_2,
    null,
    {
        pure: true,
        areStatesEqual: (next, prev) => {
            console.log(next.counterDown, prev.counterDown);
            return next.counterDown.number < prev.counterDown.number;
        }
    }
)(Decrease);

能够看到,减1的操做并无传给Decrease组件,页面没有更新

 

顺便看看有connect包装后的组件

 

4.7.8 在React-Redux中使用异步

因Redux中操做的执行是同步的,若是要实现异步,好比某个操做用来发个异步请求获取数据,就得引入中间件来处理这种特殊的操做

即这个操做再也不是普通的值,而是一个函数(如Promise异步),经过中间件的处理,让Redux可以解析

 

先修改上面的栗子,在Increase组件中再也不是每次增长1,而是根据action中的value来指定,好比

function mapDispatchToProps_1(dispatch) {
    return {
        increase: () => dispatch({
            type: 'up',
            value: 10
        })
    };
}
function couterUp(state = {number: 100}, action) {
    switch (action.type) {
        case 'up':
            return {
                // number: state.number + 1
                number: state.number + action.value
            };
        default:
            return state;
    }
}

这里定义了value是10,但假如value的值得由一个异步的请求才得出呢,要如何放进去

 

使用Redux提供的中间件applyMiddleware

let {createStore, combineReducers, applyMiddleware} = Redux;

这只是基础的中间件apply函数,它帮助Redux将中间件包装

如今来模拟一个异步请求

function mapDispatchToProps_1(dispatch) {
    return {
        // increase: () => dispatch({
        //     type: 'up',
        //     value: 10
        // })
        increase: () => dispatch(fetchIncreaseValue('redux-ajaxTest.php'))
    };
}

可一看到,dispatch中的action是一个函数(这个调用返回的仍是一个函数),而Redux默认只支持对象格式的action,因此这样会报错

这里的fetchIncreaseValue看起来像这样

function fetchIncreaseValue(url) {
    return function(dispatch) {
        return $.get(url).then(re => {
            re = JSON.parse(re);

            console.log(re);

            dispatch({
                type: 'up',
                value: re.value
            });
        })
    }
}

而请求后台后返回值

<?php

    echo json_encode(array('value' => 100));

?>

能够看到,异步获取数据以后才执行dispatch发出操做,这里须要一个dispatch关键字

为了拿到这个关键字,得和thunkMiddleware搭配使用(让这个方法可以在内层函数中使用),固然,你也能够再搭配其余中间件

若是使用Webpack打包,就安装好 redux-thunk 包再 import 进来

这里直接引入到浏览器中,引入这个库,而后直接使用(注意这里没有 {}

let thunkMiddleware = window.ReduxThunk.default;

而后在建立store的时候,传给redux的applyMiddleware便可

let store = createStore(
    couter,
    {couterUp: {number: 10}},
    applyMiddleware(thunkMiddleware),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

官方给的例子太复杂了,不过仍是去看看吧,我这里抽出了主要的部分,

先来看看结果

 

使用这个Redux Dev Tool就得在createStore中配上最后一个参数,而createStore自身的某个参数又能给reducer设置初始值,且applyMiddleware也是在参数中定义

因此要注意的是:

若是用了这个Redux Dev Tool,就要保证applyMiddleware在第三个参数

let store = createStore(
    couter,
  // {}, applyMiddleware(thunkMiddleware), window.__REDUX_DEVTOOLS_EXTENSION__
&& window.__REDUX_DEVTOOLS_EXTENSION__() );

相似这样省略第二个初始值参数,是会报错的

把注释去掉,放上一个空的初始便可,或者不用这个Dev Tool

let store = createStore(
    couter,
    applyMiddleware(thunkMiddleware)
);

能够去看看其余的Dev Tool

相关文章
相关标签/搜索