正式学习React react-redux源码分析

Posted 暗影侠客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正式学习React react-redux源码分析相关的知识,希望对你有一定的参考价值。

磨刀不误砍柴工,咱先把react-redux里的工具函数分析一下:

源码点这里

 shallowEqual.js

 1 export default function shallowEqual(objA, objB) {
 2   if (objA === objB) {
 3     return true
 4   }
 5 
 6   const keysA = Object.keys(objA)
 7   const keysB = Object.keys(objB)
 8 
 9   if (keysA.length !== keysB.length) {
10     return false
11   }
12 
13   // Test for A\'s keys different from B.
14   const hasOwn = Object.prototype.hasOwnProperty
15   for (let i = 0; i < keysA.length; i++) {
16     if (!hasOwn.call(objB, keysA[i]) ||
17         objA[keysA[i]] !== objB[keysA[i]]) {
18       return false
19     }
20   }
21 
22   return true
23 }

这个几个api全都超级简单,我就不仔细讲解了,顾名思义,简单比较一下两个obj是否相等。 

storeShape.js

1 import { PropTypes } from \'react\'
2 
3 export default PropTypes.shape({
4   subscribe: PropTypes.func.isRequired,
5   dispatch: PropTypes.func.isRequired,
6   getState: PropTypes.func.isRequired
7 })

顾名思义,强制性规定subscribe,dispacth,getState必须是func.

warning.js

 1 /**
 2  * Prints a warning in the console if it exists.
 3  *
 4  * @param {String} message The warning message.
 5  * @returns {void}
 6  */
 7 export default function warning(message) {
 8   /* eslint-disable no-console */
 9   if (typeof console !== \'undefined\' && typeof console.error === \'function\') {
10     console.error(message)
11   }
12   /* eslint-enable no-console */
13   try {
14     // This error was thrown as a convenience so that if you enable
15     // "break on all exceptions" in your console,
16     // it would pause the execution at this line.
17     throw new Error(message)
18     /* eslint-disable no-empty */
19   } catch (e) {}
20   /* eslint-enable no-empty */
21 }

就是用console.error 打印一下错误。

wrapActionCreators.js

1 import { bindActionCreators } from \'redux\'
2 
3 export default function wrapActionCreators(actionCreators) {
4   return dispatch => bindActionCreators(actionCreators, dispatch)
5 }

上一篇讲过 bindActionCreators


它返回的这个对象直接是以 我们定义单个actionCreator为key的,actionCreator函数为value的包装,并在actionCreator里挂着了dispacth的函数。使用的时候,直接调用同名key函数,就直接分发action了,不需要

我们手动的 dispacth(actionCreator(内容)), 直接key(内容) 就行了。

 

------------------------------------------------------工具全部介绍完毕-----是不是so easy??!!!------------------------------

 现在主角登场:

Provider.js

 

 1 import { Component, PropTypes, Children } from \'react\'
 2 import storeShape from \'../utils/storeShape\'
 3 import warning from \'../utils/warning\'
 4 
 5 let didWarnAboutReceivingStore = false
 6 function warnAboutReceivingStore() {
 7   if (didWarnAboutReceivingStore) {
 8     return
 9   }
10   didWarnAboutReceivingStore = true
11 
12   warning(
13     \'<Provider> does not support changing `store` on the fly. \' +
14     \'It is most likely that you see this error because you updated to \' +
15     \'Redux 2.x and React Redux 2.x which no longer hot reload reducers \' +
16     \'automatically. See https://github.com/reactjs/react-redux/releases/\' +
17     \'tag/v2.0.0 for the migration instructions.\'
18   )
19 }
20 
21 export default class Provider extends Component {

//关键部分,将this.store加到了context里,这里,子组件就可以通过context直接拿到store,不需要一级一级props传递下去。
22 getChildContext() { 23 return { store: this.store } 24 } 25 26 constructor(props, context) { 27 super(props, context) 28 this.store = props.store 29 } 30 31 render() { 32 return Children.only(this.props.children) 33 } 34 } 35 36 if (process.env.NODE_ENV !== \'production\') { 37 Provider.prototype.componentWillReceiveProps = function (nextProps) { 38 const { store } = this 39 const { store: nextStore } = nextProps 40 41 if (store !== nextStore) { 42 warnAboutReceivingStore() 43 } 44 } 45 } 46 47 Provider.propTypes = {
//要求我们的 store对象里面的3个必须是func
48 store: storeShape.isRequired,

//要求我们形如这种格式的 <Provider store={store}> <App/> </Provider> ,<App>必须是react的element,其实它还要求了只能是放单element的,这也是render这个本身限定的!!可以看上面的Children.Only()
49 children: PropTypes.element.isRequired 50 }
// 这个是和getChildContext一样,必须加的。

//访问context 的属性是需要通过 contextTypes 指定可访问的 元素一样。getChildContext 指定的传递给子组件的属性需要先通过 childContextTypes 来指定,不然会产生错误。
51 Provider.childContextTypes = { 52 store: storeShape.isRequired 53 }

 

 关于context的用法,我在github上写了一个小demo。大家可以clone下来跑一跑,在我的代码基础上可以添加一下状态函数,看看react的状态生命周期的流程。点这里

 

对于上面的代码总结一下,Provider这个React 组件就是为了将Store挂在到Context中,然后我们写的真正的app里就可以获得store了。

 connect.js


分析之前,我們心中要有這麽一個概念:

<Provider store={store}>
<App />
</Provider>,

Provider的作用就是將 store挂在到 context上 提供App使用。


重點在這个 App上。

-----------------------------------------------------------------------------------------------------
这个App不是普通的element。它经过connect包装过,
connect(select)(App)

好了,可以开始分析了,我们将知道connect是啥,select是干嘛的?

------------------------------------------------------------------------------------------------

 

  1 import { Component, createElement } from \'react\'
  2 import storeShape from \'../utils/storeShape\'
  3 import shallowEqual from \'../utils/shallowEqual\'
  4 import wrapActionCreators from \'../utils/wrapActionCreators\'
  5 import warning from \'../utils/warning\'
  6 import isPlainObject from \'lodash/isPlainObject\'
  7 import hoistStatics from \'hoist-non-react-statics\'
  8 import invariant from \'invariant\'
  9 

//一直到return 我都认为是废话,函数名字都是顾名思义的。。直接忽略。。。。 10 const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars 11 const defaultMapDispatchToProps = dispatch => ({ dispatch }) 12 const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({ 13 ...parentProps, 14 ...stateProps, 15 ...dispatchProps 16 }) 17 18 function getDisplayName(WrappedComponent) { 19 return WrappedComponent.displayName || WrappedComponent.name || \'Component\' 20 } 21 22 let errorObject = { value: null } 23 function tryCatch(fn, ctx) { 24 try { 25 return fn.apply(ctx) 26 } catch (e) { 27 errorObject.value = e 28 return errorObject 29 } 30 } 31

// 重点开始了。。。。。。 32 // Helps track hot reloading. 33 let nextVersion = 0 34 35 export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {

//这里是否应该被订阅?嗯哼,回想一下store里的 subcribe,每次被dispacth 的时候就会执行!!!,这里估计就是给一个标志,
//如果传了mapStateToProps,必然是true,反之是false;
36 const shouldSubscribe = Boolean(mapStateToProps)
37 const mapState = mapStateToProps || defaultMapStateToProps 38 39 let mapDispatch 40 if (typeof mapDispatchToProps === \'function\') { 41 mapDispatch = mapDispatchToProps 42 } else if (!mapDispatchToProps) { 43 mapDispatch = defaultMapDispatchToProps 44 } else { 45 mapDispatch = wrapActionCreators(mapDispatchToProps) 46 } 47 48 const finalMergeProps = mergeProps || defaultMergeProps 49 const { pure = true, withRef = false } = options 50 const checkMergedEquals = pure && finalMergeProps !== defaultMergeProps 51 52 // Helps track hot reloading. 53 const version = nextVersion++ 54 55 return function wrapWithConnect(WrappedComponent) {

// 如果沒有為app添加disPlayName 或者 name 返回 "Conponent"
56 const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})` 57 58 function checkStateShape(props, methodName) { 59 if (!isPlainObject(props)) { 60 warning( 61 `${methodName}() in ${connectDisplayName} must return a plain object. ` + 62 `Instead received ${props}.` 63 ) 64 } 65 } 66 67 function computeMergedProps(stateProps, dispatchProps, parentProps) { 68 const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps) 69 if (process.env.NODE_ENV !== \'production\') { 70 checkStateShape(mergedProps, \'mergeProps\') 71 } 72 return mergedProps 73 } 74 75 class Connect extends Component { 76 shouldComponentUpdate() { 77 return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged 78 } 79 80 constructor(props, context) { 81 super(props, context) 82 this.version = version 83 this.store = props.store || context.store 84 85 invariant(this.store, 86 `Could not find "store" in either the context or ` + 87 `props of "${connectDisplayName}". ` + 88 `Either wrap the root component in a <Provider>, ` + 89 `or explicitly pass "store" as a prop to "${connectDisplayName}".` 90 ) 91 92 const storeState = this.store.getState() 93 this.state = { storeState } 94 this.clearCache() 95 } 96 97 computeStateProps(store, props) { 98 if (!this.finalMapStateToProps) { 99 return this.configureFinalMapState(store, props) 100 } 101 102 const state = store.getState() 103 const stateProps = this.doStatePropsDependOnOwnProps ? 104 this.finalMapStateToProps(state, props) : 105 this.finalMapStateToProps(state) 106 107 if (process.env.NODE_ENV !== \'production\') { 108 checkStateShape(stateProps, \'mapStateToProps\') 109 } 110 return stateProps 111 } 112 113 configureFinalMapState(store, props) { 114 const mappedState = mapState(store.getState(), props) 115 const isFactory = typeof mappedState === \'function\' 116 117 this.finalMapStateToProps = isFactory ? mappedState : mapState 118 this.doStatePropsDependOnOwnProps = this.finalMapStateToProps.length !== 1 119 120 if (isFactory) { 121 return this.computeStateProps(store, props) 122 } 123 124 if (process.env.NODE_ENV !== \'production\') { 125 checkStateShape(mappedState, \'mapStateToProps\') 126 } 127 return mappedState 128 } 129 130 computeDispatchProps(store, props) { 131 if (!this.finalMapDispatchToProps) { 132 return this.configureFinalMapDispatch(store, props) 133 } 134 135 const { dispatch } = store 136 const dispatchProps = this.doDispatchPropsDependOnOwnProps ? 137 this.finalMapDispatchToProps(dispatch, props) : 138 this.finalMapDispatchToProps(dispatch) 139 140 if (process.env.NODE_ENV !== \'production\') { 141 checkStateShape(dispatchProps, \'mapDispatchToProps\') 142 } 143 return dispatchProps 144 } 145 146 configureFinalMapDispatch(store, props) { 147 const mappedDispatch = mapDispatch(store.dispatch, props) 148 const isFactory = typeof mappedDispatch === \'function\' 149 150 this.finalMapDispatchToProps = isFactory ? mappedDispatch : mapDispatch 151 this.doDispatchPropsDependOnOwnProps = this.finalMapDispatchToProps.length !== 1 152 153 if (isFactory) { 154 return this.computeDispatchProps(store, props) 155 } 156 157 if (process.env.NODE_ENV !== \'production\') { 158 checkStateShape(mappedDispatch, \'mapDispatchToProps\') 159 } 160 return mappedDispatch 161 } 162 163 updateStatePropsIfNeeded() { 164 const nextStateProps = this.computeStateProps(this.store, this.props) 165 if (this.stateProps && shallowEqual(nextStateProps, this.stateProps)) { 166 return false 167 } 168 169 this.stateProps = nextStateProps 170 return true 171 } 172 173 updateDispatchPropsIfNeeded() { 174 const nextDispatchProps = this.computeDispatchProps(this.store, this.props) 175 if (this.dispatchProps && shallowEqual(nextDispatchProps, this.dispatchProps)) { 176 return false 177 } 178 179 this.dispatchProps = nextDispatchProps 180 return true 181 } 182 183 updateMergedPropsIfNeeded() { 184 const nextMergedProps = computeMergedProps(this.stateProps, this.dispatchProps, this.props) 185 if (this.mergedProps && checkMergedEquals && shallowEqual(nextMergedProps, this.mergedProps)) { 186 return false 187 } 188 189 this.mergedProps = nextMergedProps 190 return true 191 } 192 193 isSubscribed() { 194 return typeof this.unsubscribe === \'function\' 195 } 196 197 trySubscribe() { 198 if (shouldSubscribe && !this.unsubscribe) { 199 this.unsubscribe = this.store.subscribe(this.handleChange.bind(this)) 200 this.handleChange() 201 } 202 } 203 204 tryUnsubscribe() { 205 if (this.unsubscribe) { 206 this.unsubscribe() 207 this.unsubscribe = null 208 } 209 } 210 211 componentDidMount() { 212 this.trySubscribe() 213 } 214 215 componentWillReceiveProps(nextProps) { 216 if (!pure || !shallowEqual(nextProps, this.props)) { 217 this.haveOwnPropsChanged = true 218 } 219 } 220 221 componentWillUnmount() { 222 this.tryUnsubscribe() 223 this.clearCache() 224 } 225 226 clearCache() { 227 this.dispatchProps = null 228 this.stateProps = null 229 this.mergedProps = null 230 this.haveOwnPropsChanged = true 231 this.hasStoreStateChanged = true 232 this.haveStatePropsBeenPrecalculated = false 233 this.statePropsPrecalculationError = null 234 this.renderedElement = null 235 this.finalMapDispatchToProps = null 236 this.finalMapStateToProps = null 237 } 238 239 handleChange() { 240 if (!this.unsubscribe) { 241 return 242 } 243 244 const storeState = this.store.getState() 245 const prevStoreState = this.state.storeState 246 if (pure && prevStoreState === storeState) { 247 return 248 } 249 250 if (pure && !this.doStatePropsDependOnOwnProps) { 251 const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this) 252 if (!haveStatePropsChanged) { 253 return 254 } 255 if (haveStatePropsChanged === errorObject) { 256 this.statePropsPrecalculationError = errorObject.value 257 } 258 this.haveStatePropsBeenPrecalculated = true 259 } 260 261 this.hasStoreStateChanged = true 262 this.setState({ storeState }) 263 } 264 265 getWrappedInstance() { 266 invariant(withRef, 267 `To access the wrapped instance, you need to specify ` + 268 `{ withRef: true } as the fourth argument of the connect() call.` 269 ) 270 271 return this.refs.wrappedInstance 272 } 273 274 render() { 275 const { 276 haveOwnPropsChanged, 277 hasStoreStateChanged, 278 haveStatePropsBeenPrecalculated, 279 statePropsPrecalculationError, 280 renderedElement 281 } = this 282 283 this.haveOwnPropsChanged = false 284 this.hasStoreStateChanged = false 285 this.haveStatePropsBeenPrecalculated = false 286 this.statePropsPrecalculationError = null 287 288 if (statePropsPrecalculationError) { 289 throw statePropsPrecalculationError 290 } 291 292 let shouldUpdateStateProps = true 293 let shouldUpdateDispatchProps = true 294 if (pure && renderedElement) { 295 shouldUpdateStateProps = hasStoreStateChanged || ( 296 haveOwnPropsChanged && this.doStatePropsDependOnOwnProps 297 ) 298 shouldUpdateDispatchProps = 299 haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps 300 } 301 302 let haveStatePropsChanged = false 303 let haveDispatchPropsChanged = false 304 if (haveStatePropsBeenPrecalculated) { 305 haveStatePropsChanged = true 306 } else if (shouldUpdateStateProps) { 307 haveStatePropsChanged = this.updateStatePropsIfNeeded() 308 } 309 if (shouldUpdateDispatchProps) { 310 haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded() 311 } 312 313 let haveMergedPropsChanged = true 314 if ( 315 haveStatePropsChanged || 316 haveDispatchPropsChanged || 317 haveOwnPropsChanged 318 ) { 319 haveMergedPropsChanged = this.updateMergedPropsIfNeeded() 320 } else { 321 haveMergedPropsChanged =

以上是关于正式学习React react-redux源码分析的主要内容,如果未能解决你的问题,请参考以下文章

正式学习React ----Redux源码分析

redux ,react-redux梳理,以及源码的学习

react-redux 源码浅析

react-redux 源码浅析

正式学习 react

react 中的 redux 和react-redux的区别分析