redux——入门2
在redux——入门1里面,我简要介绍了redux的核心概念,并举了一个简单的计数器demo的例子,用于展示在react中怎么使用redux。现在,我打算把这个简单的demo变得复杂一点,引入react-redux和redux中一些其他的概念。
工具分享
在本篇正式开始之前,我想先分享一个用于快速构建react应用的脚手架工具,传送门:https://github.com/facebookincubator/create-react-app
概念引入
我会在这个升级版的demo里面,引入三个东西,Provider(react-redux),combineReducers(redux),connect(react-redux),如果你想更深入的了解react-redux中的各个角色,这里有个很好的解释https://github.com/jasonslyvia/a-cartoon-intro-to-redux-cn
Provider
这是一个组件,没有其他特殊的作用,但是我们需要将它包裹在整个组件树的最外层,只有这样,其内部的子孙组件才能使用connect来绑定store。
connect
这是一个函数,由react-redux提供,其返回依然是一个函数,该函数会处理视图与store绑定的细节,具体的使用方法后面会做介绍。
combineReducers
这也是一个函数。在上一章,我们的reducer是一个单一的函数,在处理类似于计数器这样的简单应用时,我们不会看出有什么问题,但是当整个系统变得复杂后,单一的reducer就会变得臃肿不堪,所以我们需要对reducer进行分片,每一个reducer用于单独处理一部分state,而combineReducers就是将分片的reducer合并为一个整体,这个函数的实现也比较简单。
counter升级版
因为参照了react的官方示例,因此整个例子所使用的语法和上个简化版的例子会有比较大的出入。
入口文件index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import reducer from './reducers';
import Root from './components/root';
let store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<Root></Root>
</Provider>,
document.getElementById('root')
);
同简化版本相比,这里我们引入了Provider,并将其包裹在了组件的最外层。
reducers/index.js
import {combineReducers} from 'redux';
import counter from './counter';
const all = combineReducers({
count: counter
});
export default all;
在这里,我们调用了combineReducers方法,将counter这个reducer合并到主reducer上,因为计数器这个demo很简单,所以我们只将state划分了一个属性count,而counter这个reducer和简化版的没有什么区别,姑且还是贴一下代码
export default (state=0, action) => {
switch(action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
前面说过,combineReducer的实现其实是蛮简单的,其实就是返回一个函数,每次处理action的时候,这个函数会遍历所有的reducer来处理这个action,然后将所有结果打包返回,代码和下面类似
// combineReducers
const combineReducers = ( reducers ) => {
return ( state = {}, action ) => {
return Object.keys(reducers).reduce(
( nextState, key ) => {
nextState[key] = reducers[key](
state[key],
action
);
return nextState;
},
{}
);
};
};
components/root.js
这是我们的根组件
import React from 'react';
import InputBox from '../containers/input-box';
import ShowBox from '../containers/show-box';
export default () => {
return (
<div>
<InputBox></InputBox>
<ShowBox></ShowBox>
</div>
);
};
这个没什么可以讲的,主要看接下来的InputBox和ShowBox
containers/input-box
这是一个纯输入组件
import React from 'react';
import {connect} from 'react-redux';
let InputBox = ({onIncrement, onDecrement}) => {
return (
<div>
<button type="button" onClick={onIncrement}>+++</button>
<button type="button" onClick={onDecrement}>---</button>
</div>
);
};
let mapDispatchToProps = (dispatch) => ({
onIncrement: () => dispatch({type: 'INCREMENT'}),
onDecrement: () => dispatch({type: 'DECREMENT'})
});
InputBox = connect(undefined, mapDispatchToProps)(InputBox);
export default InputBox;
在这里我们调用了react-redux的connect方法,当然,如果之前完全没接触过connect函数,看这段代码可能会有点头疼,可以先移步这里connect的api文档。我也可以简单介绍下connect的使用方法,它支持四个参数,我这里只介绍前两个,后两个因为我并没怎么用过,所以暂时不讲。
第一个参数是mapStateToProps(state, [ownProps])
,用于选择性的将state中的属性注入到组件的props中,我在show-box中使用了这个参数,所以请看后面的代码。
第二个参数是mapDispatchToProps(dispatch, [ownProps])
,用于将需要触发dispatch的方法,注入到组建的props中,在上面的input-box中,我使用了这个参数,将onIncrement
和onDecrement
两个用于dispatch的方法注入到了InputBox中。
container/show-box
这是一个纯展示的组件
import React from 'react';
import {connect} from 'react-redux';
let ShowBox = ({count}) => {
return (
<div>
<p>{count}</p>
</div>
);
};
let mapStateToProps = (state) => ({
count: state.count
});
ShowBox = connect(mapStateToProps)(ShowBox);
export default ShowBox;
唯一需要注意的是用了mapStateToProps
总结
虽然是说是升级版,但也只是多引入了几个东西,总体来说还是算简单,只是概念多了,容易让人糊涂,我被绕了一个上午,好在现在总算有点清醒了,所以记下这些东西,方便之后的回顾。