Flux --> Redux --> Redux React 入门 基础实例使用

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

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

一般来说,推荐使用 ES6+React+Webpack 的开发模式,但Webpack需要配置一些东西,你可以先略过,本文不需要Webpack基础

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

(不过我个人认为,官方文档的例子相对来说太复杂了,很难让新手马上抓住重点)

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

搭飞机前往:

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

一、Flux

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

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

此应用应该分为四层:

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

它的概念思想可能一时半会理解不了,没关系,过段时间就好了

二、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     }

加上个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组件)

而一般来说,一般来说会传入两个参数(支持四个参数),顾名思义:

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

如果不传或置入undefined或null,则表示不需要进行数据更新;否则表示将store中的数据通过props的形式传给React组件

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

如果不传或置入undefined或null,则表示将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方法,第二个表示自身拥有的 propsDispatch方法(即类似省略参数传给字组件的默认dispatch方法)

最后它返回了一个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‘)
);

看一下运行结果

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 );

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

时间: 2024-08-02 19:12:59

Flux --> Redux --> Redux React 入门 基础实例使用的相关文章

react入门----基础语法

1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <!-- react核心库 --> 7 <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js&quo

React入门----基础篇

React 背景介绍 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了. 特点 1 使用 JSX语法 创建组件,实现组件化开发,为函数式的 UI 编程方式打开了大门 2 性能高的让人称赞:通过 diff算法 和 虚拟DOM 实现视图的高效更新 3 HTML仅仅是个开始 为什么要用React 1 使用组件化开发方式,

React入门-高清视频

一.ReactJS简介 React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了.由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单.所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具. ReactJS官网地址:http://facebook.github.io/re

【转】浅谈React、Flux 与 Redux

本文转自<浅谈React.Flux 与 Redux>,转载请注明出处. React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化 利用 props 形成单向的数据流 根据 state 的变化来更新 view 利用虚拟 DOM 来提升渲染性能 前面说到 React 能够根据 state 的变化来更新 view,一般来说引起 state 变化的动作除了来自外部(如服务器),大部分都来自于页面上的用户活动,那页面上的用户活动怎样对 state 产生作用呢?Reac

浅谈 React、Flux 与 Redux

React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化利用 props 形成单向的数据流根据 state 的变化来更新 view利用虚拟 DOM 来提升渲染性能 前面说到 React 能够根据 state 的变化来更新 view,一般来说引起 state 变化的动作除了来自外部(如服务器),大部分都来自于页面上的用户活动,那页面上的用户活动怎样对 state 产生作用呢?React 中每个组件都有 setState 方法用于改变组件当前的 state,所以可

React Native 入门基础知识总结

中秋在家闲得无事,想着做点啥,后来想想,为啥不学学 react native.在学习 React Native 时, 需要对前端(HTML,CSS,JavaScript)知识有所了解.对于JS,可以看看阮一峰老师的<ECMAScript 6 入门>这篇文章.里面涉及很多 ES6 的新特性.我之前也是看了阮老师的文章做了一些学习笔记 ES6 学习笔记. 1.环境搭建 环境搭建中文教程,点击跳转RN中文社区 :http://reactnative.cn/docs/0.40/getting-star

从Flux到Redux详解单项数据流

从Flux到Redux是状态管理工具的演变过程,但两者还是有细微的区别的.但是最核心的都还是观察者模式的应用. 一.Flux 1. Flux的处理逻辑 通俗来讲,应用的状态被放到了store中,组件是store状态的一个映射,用户通过事件触发action,再通过Dispatcher根据不同的actionType进行分发,并做不同的逻辑处理,但核心都是通过直接改变store的状态,再触发emitChange间接改变组件中的数据.(后面会上代码) 从代码层面来讲,store中的数据通过EventEm

React 入门实例教程

React 入门实例教程 作者: 阮一峰 日期: 2015年3月31日 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了. 由于 React 的

(转)React 入门实例教程

作者: 阮一峰 现在最热门的前端框架,毫无疑问是 React . 上周,基于 React 的 React Native 发布,结果一天之内,就获得了 5000 颗星,受瞩目程度可见一斑. React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西很好用,就在2013年5月开源了. 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非