|
| 1 | +``` |
| 2 | +import React, {Component, PropTypes} from 'react'; |
| 3 | +
|
| 4 | +import ReactDOM, {render} from 'react-dom'; |
| 5 | +
|
| 6 | +import {Provider, connect} from 'react-redux'; |
| 7 | +
|
| 8 | +import {createStore, combineReducers, applyMiddleware} from 'redux'; |
| 9 | +
|
| 10 | +import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router'; |
| 11 | +``` |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +redux主要由三部分组成: |
| 16 | +>store,reducer,action。 |
| 17 | +
|
| 18 | +### **store** |
| 19 | +是一个对象,它有四个主要的方法: |
| 20 | + |
| 21 | +**1、dispatch:** |
| 22 | + |
| 23 | +> 用于action的分发——在createStore中可以用middleware中间件对dispatch进行改造,比如当action传入dispatch会立即触发reducer,有些时候我们不希望它立即触发,而是等待异步操作完成之后再触发,这时候用redux-thunk对dispatch进行改造,以前只能传入一个对象,改造完成后可以传入一个函数,在这个函数里我们手动dispatch一个action对象,这个过程是可控的,就实现了异步。 |
| 24 | +
|
| 25 | +**2、subscribe:** |
| 26 | + |
| 27 | +> 监听state的变化——这个函数在store调用dispatch时会注册一个listener监听state变化,当我们需要知道state是否变化时可以调用,它返回一个函数,调用这个返回的函数可以注销监听。 |
| 28 | +
|
| 29 | +let unsubscribe = store.subscribe(() => {console.log('state发生了变化')}) |
| 30 | + |
| 31 | +**3、getState:** |
| 32 | + |
| 33 | +> 获取store中的state——当我们用action触发reducer改变了state时,需要再拿到新的state里的数据,毕竟数据才是我们想要的。getState主要在两个地方需要用到,一是在dispatch拿到action后store需要用它来获取state里的数据,并把这个数据传给reducer,这个过程是自动执行的,二是在我们利用subscribe监听到state发生变化后调用它来获取新的state数据,如果做到这一步,说明我们已经成功了。 |
| 34 | +
|
| 35 | +**4、replaceReducer:** |
| 36 | + |
| 37 | +> 替换reducer,改变state修改的逻辑。 |
| 38 | +
|
| 39 | +store可以通过createStore()方法创建,接受三个参数,经过combineReducers合并的reducer和state的初始状态以及改变dispatch的中间件,后两个参数并不是必须的。store的主要作用是将action和reducer联系起来并改变state。 |
| 40 | + |
| 41 | +### **action:** |
| 42 | + |
| 43 | +>action是一个对象,其中type属性是必须的,同时可以传入一些数据。action可以用actionCreactor进行创造。dispatch就是把action对象发送出去。 |
| 44 | +
|
| 45 | +### **reducer:** |
| 46 | + |
| 47 | +>reducer是一个函数,它接受一个state和一个action,根据action的type返回一个新的state。根据业务逻辑可以分为很多个reducer,然后通过combineReducers将它们合并,state树中有很多对象,每个state对象对应一个reducer,state对象的名字可以在合并时定义。 |
| 48 | +
|
| 49 | +像这个样子: |
| 50 | +``` |
| 51 | +const reducer = combineReducers({ |
| 52 | +
|
| 53 | + a: doSomethingWithA, |
| 54 | +
|
| 55 | + b: processB, |
| 56 | +
|
| 57 | + c: c |
| 58 | +
|
| 59 | +}) |
| 60 | +``` |
| 61 | + |
| 62 | +**combineReducers:** |
| 63 | + |
| 64 | +>其实它也是一个reducer,它接受整个state和一个action,然后将整个state拆分发送给对应的reducer进行处理,所有的reducer会收到相同的action,不过它们会根据action的type进行判断,有这个type就进行处理然后返回新的state,没有就返回默认值,然后这些分散的state又会整合在一起返回一个新的state树。 |
| 65 | +
|
| 66 | +接下来分析一下整体的流程,首先调用store.dispatch将action作为参数传入,同时用getState获取当前的状态树state并注册subscribe的listener监听state变化,再调用combineReducers并将获取的state和action传入。combineReducers会将传入的state和action传给所有reducer,reducer会根据state的key值获取与自己对应的state,并根据action的type返回新的state,触发state树的更新,我们调用subscribe监听到state发生变化后用getState获取新的state数据。 |
| 67 | + |
| 68 | + |
| 69 | +## React-Redux |
| 70 | + |
| 71 | +如果只使用redux,那么流程是这样的: |
| 72 | + |
| 73 | +> component --> dispatch(action) --> reducer --> subscribe --> getState --> component |
| 74 | +
|
| 75 | +用了react-redux之后流程是这样的: |
| 76 | + |
| 77 | +> component --> actionCreator(data) --> reducer --> component |
| 78 | +
|
| 79 | +store的三大功能:dispatch,subscribe,getState都不需要手动来写了。 |
| 80 | +react-redux帮我们做了这些,同时它提供了两个好基友Provider和connect。 |
| 81 | + |
| 82 | +### **Provider** |
| 83 | +```javascript |
| 84 | +import { Provider } from 'react-redux'; // 引入 react-redux |
| 85 | + |
| 86 | +// …… |
| 87 | +render( |
| 88 | + <Provider store={store}> |
| 89 | + <Sample /> |
| 90 | + </Provider>, |
| 91 | + document.getElementById('app'), |
| 92 | +); |
| 93 | +``` |
| 94 | +是一个组件,它接受store作为props,然后通过context往下传,这样react中任何组件都可以通过contex获取store。也就意味着我们可以在任何一个组件里利用dispatch(action)来触发reducer改变state,并用subscribe监听state的变化,然后用getState获取变化后的值。但是并不推荐这样做,它会让数据流变的混乱,过度的耦合也会影响组件的复用,维护起来也更麻烦。 |
| 95 | + |
| 96 | +### **connect** |
| 97 | + |
| 98 | +**`connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])`** |
| 99 | + |
| 100 | +是一个函数,它接受四个参数并且再返回一个函数--wrapWithConnect,wrapWithConnect接受一个组件作为参数wrapWithConnect(component),它内部定义一个新组件Connect(容器组件)并将传入的组件(ui组件)作为Connect的子组件然后return出去。 |
| 101 | + |
| 102 | +所以它的完整写法是这样的:connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component) |
| 103 | + |
| 104 | +``` |
| 105 | +// 例子1 |
| 106 | +const mapStateToProps = (state) => { |
| 107 | + return { |
| 108 | + number: state.changeNumber.number, |
| 109 | + showAlert: state.toggleAlert.showAlert, |
| 110 | + }; |
| 111 | +}; |
| 112 | +// 例子2 |
| 113 | +const mapStateToProps = (state, ownProps) => { |
| 114 | + // state 是 {userList: [{id: 0, name: 'Jack'}, ...]} |
| 115 | + return { |
| 116 | + isMe: state.userList.includes({id: ownProps.userId}) |
| 117 | + }; |
| 118 | +} |
| 119 | +
|
| 120 | +/* |
| 121 | +在组件中通过this.props.incrementNum()方式来dispatch Action出去,通知Reducer修改state。 |
| 122 | +如果你对Action比较熟悉的话,可能会疑惑,this.props.incrementNum()只是生成了一个Action,应该是写成:dispatch(this.props. incrementNum())才对吧? |
| 123 | +继续看下面介绍的function形式的mapDispatchToProps就能明白,其实dispatch已经被connect封装进去了,因此你不必手动写dispatch了。 |
| 124 | +*/ |
| 125 | +const mapDispatchToProps = { |
| 126 | + incrementNum: action.number.incrementNum, |
| 127 | + decrementNum: action.number.decrementNum, |
| 128 | + clearNum: action.number.clearNum, |
| 129 | + toggleAlert: action.alert.toggleAlert, |
| 130 | +}; |
| 131 | +
|
| 132 | +// 例子2 |
| 133 | +import { bindActionCreators } from 'redux'; |
| 134 | +const mapDispatchToProps = (dispatch, ownProps) => { |
| 135 | + return { |
| 136 | + incrementNum: bindActionCreators(action.number.incrementNum, dispatch), |
| 137 | + decrementNum: bindActionCreators(action.number.decrementNum, dispatch), |
| 138 | + clearNum: bindActionCreators(action.number.clearNum, dispatch), |
| 139 | + toggleAlert: bindActionCreators(action.alert.toggleAlert, dispatch), |
| 140 | + }; |
| 141 | +}; |
| 142 | +
|
| 143 | +const mergeProps = (stateProps, dispatchProps, ownProps) => { |
| 144 | + return { |
| 145 | + ...ownProps, |
| 146 | + ...stateProps, |
| 147 | + incrementNum: dispatchProps.incrementNum, // 只输出incrementNum |
| 148 | + }; |
| 149 | +}; |
| 150 | +
|
| 151 | +export default connect( |
| 152 | + mapStateToProps, |
| 153 | + mapDispatchToProps, |
| 154 | + mergeProps, |
| 155 | +)(Sample); |
| 156 | +``` |
| 157 | + |
| 158 | +**`mapStateToProps(state, [ownProps])`:** |
| 159 | + |
| 160 | +>mapStateToProps 接受两个参数,store的state和自定义的props,并返回一个新的对象, |
| 161 | +这个对象会作为props的一部分传入ui组件。 |
| 162 | +我们可以根据组件所需要的数据自定义返回一个对象。 |
| 163 | +ownProps,看名字就知道是组件自己原始的props(即不包含connect后新增的props) |
| 164 | + |
| 165 | + |
| 166 | +**`mapDispatchToProps(dispatch, [ownProps])`:** |
| 167 | + |
| 168 | +> mapDispatchToProps如果是对象,那么会和store绑定作为props的一部分传入ui组件。 |
| 169 | +如果是个函数,它接受两个参数,bindActionCreators会将action和dispatch绑定并返回一个对象,这个对象会和ownProps一起作为props的一部分传入ui组件。 |
| 170 | +所以不论mapDispatchToProps是对象还是函数,它最终都会返回一个对象,如果是函数,这个对象的key值是可以自定义的 |
| 171 | +第二个可选参数ownProps和mapStateToProps里作用是一样的,不赘述。 |
| 172 | + |
| 173 | +function mapDispatchToProps(dispatch) { |
| 174 | + |
| 175 | + return { |
| 176 | + |
| 177 | + todoActions: bindActionCreators(todoActionCreators, dispatch), |
| 178 | + |
| 179 | + counterActions: bindActionCreators(counterActionCreators, dispatch) |
| 180 | + |
| 181 | + }; |
| 182 | + |
| 183 | +} |
| 184 | +mapDispatchToProps返回的对象其属性其实就是一个个actionCreator,因为已经和dispatch绑定,所以当调用actionCreator时会立即发送action,而不用手动dispatch。ownProps的变化也会触发mapDispatchToProps。 |
| 185 | + |
| 186 | +**`mergeProps(stateProps, dispatchProps, ownProps)`:** |
| 187 | + |
| 188 | +> 将mapStateToProps() 与 mapDispatchToProps()返回的对象和组件自身的props合并成新的props并传入组件。 |
| 189 | +默认返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。 |
| 190 | +经过conncet的组件的props有3个来源:一是由mapStateToProps将state映射成的props,二是由mapDispatchToProps将Action creator映射成的props,三是组件自身的props。 |
| 191 | +connect的第三个参数mergeProps也是一个function:`[mergeProps(stateProps, dispatchProps, ownProps): props]`, |
| 192 | +参数分别对应了上述props的3个来源,作用是整合这些props。例如过滤掉不需要的props: |
| 193 | + |
| 194 | +**options:** |
| 195 | + |
| 196 | +> pure = true 表示Connect容器组件将在shouldComponentUpdate中对store的state和ownProps进行浅对比,判断是否发生变化,优化性能。为false则不对比。 |
| 197 | +
|
| 198 | +其实connect函数并没有做什么,大部分的逻辑都是在它返回的wrapWithConnect函数内实现的,确切的说是在wrapWithConnect内定义的Connect组件里实现的。 |
0 commit comments