本文的目的很简单,介绍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方法,第二个表示自身拥有的属性(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')
);
看一下运行结果
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包装后的组件
因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