• 正式学习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 = false 322 } 323 324 if (!haveMergedPropsChanged && renderedElement) { 325 return renderedElement 326 } 327 328 if (withRef) { 329 this.renderedElement = createElement(WrappedComponent, { 330 ...this.mergedProps, 331 ref: 'wrappedInstance' 332 }) 333 } else { 334 this.renderedElement = createElement(WrappedComponent, 335 this.mergedProps 336 ) 337 } 338 339 return this.renderedElement 340 } 341 } 342 343 Connect.displayName = connectDisplayName 344 Connect.WrappedComponent = WrappedComponent 345 Connect.contextTypes = { 346 store: storeShape 347 } 348 Connect.propTypes = { 349 store: storeShape 350 } 351 352 if (process.env.NODE_ENV !== 'production') { 353 Connect.prototype.componentWillUpdate = function componentWillUpdate() { 354 if (this.version === version) { 355 return 356 } 357 358 // We are hot reloading! 359 this.version = version 360 this.trySubscribe() 361 this.clearCache() 362 } 363 } 364 365 return hoistStatics(Connect, WrappedComponent) 366 } 367 }

    妈蛋!!!这次有点长,不过不要慌!抓主干!为了更容易理解,我打算从React的生命周期函数的执行顺序讲解!!相信看到我这篇文章的时候,你已经对React有一定的了解,如果还不清楚

    React的生命周期函数的执行顺序,看我的正式学习React(三) 和 正式学习React(三)番外篇!或者去官网,或者去谷歌吧。默认我就当大家都知道了!!

    1:首先我们要知道connect函数返回的是一个包装类的func,为什么叫它包装类的func,因为它对我们App组件进一步封装,你可以看到的

    <Provider store={store}>

           <App/>

    </Porvider>

    其实是:

    <Provider store={store}>

        <connect>

             <App/>

        </connect>

    </Porvider>

    2:connect方法声明如下:

    connect([mapStateToProps], [mapDispatchToProps], [mergeProps],[options])

    作用:连接 React 组件与 Redux store

    连接操作不会改变原来的组件类,反而返回一个新的已与 Redux store 连接的组件类。 【就是我第一点说明的,加了个connect组件,将store和app关联了起来,其实是和connect关联了起来!】

    参数

    [mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生改变,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果你省略了这个参数,你的组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数 ownProps,则该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToProps 也会被调用。

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

    我简单补充一下:为什么组件会监听 Redux store的变化?flux的设计理念就是 dispatch ----》reducer--------》setState。 这个函数是将当前的state映射到props上去.

    要想更新视图,使组件渲染成DOM都是最新state和props。必须调用setState,那为什么会调用setState,必然是调用了dispatch,那我们在前面的文章里提过,dispacth的时候,会得到最新 state,并且会

    调用subscribe里所有的监听函数。那我们的setState就在这些监听函数里调用!!!

     

    一会你就会看到 componentDidMount()里调用了subcribe(handlechange)  handlechange里最终会调用mapStateToProps,最后 this.setState({ storeState })

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

    [mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,而且这个对象会与 Redux store 绑定在一起,其中所定义的方法名将作为属性名,合并到组件的 props 中。如果传递的是一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如何返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起(提示:你也许会用到 Redux 的辅助函数 bindActionCreators())。如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中。如果指定了该回调函数中第二个参数 ownProps,该参数的值为传递到组件的 props,而且只要组件接收到新 props,mapDispatchToProps 也会被调用。

     

    [mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。

     

    [options] (Object) 如果指定这个参数,可以定制 connector 的行为。

    [pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。

    [withRef = false] (Boolean): 如果为 true,connector 会保存一个对被包装组件实例的引用,该引用通过 getWrappedInstance() 方法获得。默认值为 false

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

    3: wrapWithConnect(WrappedComponent){}【主干来了!!!】

    return hoistStatics(Connect, WrappedComponent);

    hoistStatics不懂没关系,下一篇文章我会讲解。你现在就当它返回Connect,这个Connect是拥有一些WrappedComponent的props的。

    这个函数体里我们关心的就是class connect了。

    下面我将以生命周期函数的执行顺序讲解 connect!!!!

       1:首先我们先去它的constructor里看一看:

       

     1             constructor(props, context) {
     2                 super(props, context)
     3                 this.version = version
     4 
     5                 //来了!来了! context.store也加到了Connect里啦!!这样就保证了Provider和connect里公用了一个store!
     6                 this.store = props.store || context.store
     7                 //这个函数的代码直接忽略好了
     8                 invariant(this.store,
     9                     `Could not find "store" in either the context or ` +
    10                     `props of "${connectDisplayName}". ` +
    11                     `Either wrap the root component in a <Provider>, ` +
    12                     `or explicitly pass "store" as a prop to "${connectDisplayName}".`
    13                 )
    14 
    15                 //获取当前state
    16                 const storeState = this.store.getState()
    17 
    18                 //注意看  this.state,可想而知,之后会在某个地方调用setState 更新视图。整个React-redux项目,将会在connect这里发起更新视图的发起点,因为这里的才有state,子组件我们都是通过props传下去的
    19                 this.state = { storeState }
    20                 this.clearCache()   //这里初始化一些connect的实例属性。里面的具体属性自己看源码,他们的用途,在接下来的用到的时候再讲
    21             }

    看完构造函数,我们就最大的收获就是store成为了connect实例的一个属性!并在这里加了state属性!然后初始化了一些属性。。。。。

      2:render(){}  这个函数我会讲两次,一次是我们第一次初始化的时候执行,另外就是在Mounted下,我们先看第一次,具体过程大家自己看注释,一步一步走:

     1  render() {
     2                 //这里我们一开始调用了clearCache 给 connect添加了这些属性,并赋予了初始值。
     3                 //这里单独用变量取出来用作下面的操作!
     4                 const {
     5                     haveOwnPropsChanged,  //true
     6                     hasStoreStateChanged, //true
     7                     haveStatePropsBeenPrecalculated, //false
     8                     statePropsPrecalculationError, //null
     9                     renderedElement  // null
    10                     } = this
    11 
    12                 //这里给属性还原
    13                 this.haveOwnPropsChanged = false
    14                 this.hasStoreStateChanged = false
    15                 this.haveStatePropsBeenPrecalculated = false
    16                 this.statePropsPrecalculationError = null
    17 
    18 
    19                 //第一次会跳过。因为statePropsPrecalculationError == null
    20                 if (statePropsPrecalculationError) {
    21                     throw statePropsPrecalculationError
    22                 }
    23 
    24                 //这里都执行render来更新视图了,也很好理解这2个变量。
    25                 //肯定是认为应该更新state和proos了
    26                 //也要把props分发给子组件了
    27                 let shouldUpdateStateProps = true
    28                 let shouldUpdateDispatchProps = true
    29 
    30                 //第一次明显跳过 ,因为renderedElement == null
    31                 if (pure && renderedElement) {
    32                     shouldUpdateStateProps = hasStoreStateChanged || (
    33                             haveOwnPropsChanged && this.doStatePropsDependOnOwnProps
    34                         )
    35                     shouldUpdateDispatchProps =
    36                         haveOwnPropsChanged && this.doDispatchPropsDependOnOwnProps
    37                 }
    38 
    39 
    40 
    41                 let haveStatePropsChanged = false
    42                 let haveDispatchPropsChanged = false
    43 
    44                 if (haveStatePropsBeenPrecalculated) {
    45                     haveStatePropsChanged = true
    46                 } else if (shouldUpdateStateProps) {
    47 
    48                     //这里厉害了!因为我们要开始调用我们connect的第一个参数了!
    49                     haveStatePropsChanged = this.updateStatePropsIfNeeded()
    50                 }
    51                 // 第一遍shouldUpdateDispatchProps总是true,haveDispatchPropsChanged总是ture。
    52                 if (shouldUpdateDispatchProps) {
    53 
    54                     haveDispatchPropsChanged = this.updateDispatchPropsIfNeeded()
    55                 }
    56 
    57                 let haveMergedPropsChanged = true
    58 
    59                 // 第一遍
    60                 if (
    61                     haveStatePropsChanged ||
    62                     haveDispatchPropsChanged ||
    63                     haveOwnPropsChanged
    64                 ) {
    65                     haveMergedPropsChanged = this.updateMergedPropsIfNeeded()
    66                 } else {
    67                     haveMergedPropsChanged = false
    68                 }
    69                
    70                 
    71                 //没变化就返回之前的组件。
    72                 if (!haveMergedPropsChanged && renderedElement) {
    73                     return renderedElement
    74                 }
    75                
    76                 
    77                 //不然就将mergedProps 当新的props给App
    78                 if (withRef) {
    79                     this.renderedElement = createElement(WrappedComponent, {
    80                         ...this.mergedProps,
    81                         ref: 'wrappedInstance'
    82                     })
    83                 } else {
    84                     
    85                     this.renderedElement = createElement(WrappedComponent,
    86                         this.mergedProps
    87                     )
    88                 }
    89 
    90                 return this.renderedElement
    91             }
    View Code

    3:componentDidMount(){} 

    render完成之后就到了这一步。这是生命函数的固定顺序!

    里面很简单,就是调用了trySubscribe(){}

    trySubscribe这个函数我有必要讲一下:
     1     trySubscribe() {
     2                 // shouldSubscribe 是干嘛的??我们先不管它,假设条件成立!
     3                 if (shouldSubscribe && !this.unsubscribe) {
     4 
     5                     //开始订阅函数啦! 这是关键呀,以后我们更新视图全在handleChange里进行
     6                     this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
     7 
     8                     this.handleChange()
     9                 }
    10             }
    11 
    12 
    13             //如果state或者props发生改变,就调用setState。
    14             handleChange() {
    15                 if (!this.unsubscribe) {
    16                     return
    17                 }
    18 
    19                 const storeState = this.store.getState()
    20                 const prevStoreState = this.state.storeState
    21 
    22 
    23 
    24                 if (pure && prevStoreState === storeState) {
    25                     return
    26                 }
    27 
    28                 if (pure && !this.doStatePropsDependOnOwnProps) {
    29                     const haveStatePropsChanged = tryCatch(this.updateStatePropsIfNeeded, this)
    30                     if (!haveStatePropsChanged) {
    31                         return
    32                     }
    33                     if (haveStatePropsChanged === errorObject) {
    34                         this.statePropsPrecalculationError = errorObject.value
    35                     }
    36                     this.haveStatePropsBeenPrecalculated = true
    37                 }
    38 
    39                 this.hasStoreStateChanged = true
    40                 this.setState({ storeState })
    41             }
    42 
    43   // shouldSubscribe 为false,直接第一次渲染后不再更新视图了
    44   
    45   //   const shouldSubscribe = Boolean(mapStateToProps)
    46  // mapStateToProps的重要性也凸显出来了!!!
    View Code

    4:如果上面的setState被调用,就到了

    shouldComponentUpdate() {

    //不纯的默认更新

    // this.haveOwnPropsChanged == true 发生在第一次构造connect 的时候,还有就是props被修改的时候。

    //
    return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
    }

    出场了。。。。。。然后看不是要继续render()




    5:componentWillReceiveProps(nextProps){}  当调用了dispath的时候,就会触发setState,然后就可能会触发这个。。。。

    6:componentWillUnmount(){}   这个把刚才订阅取消。




    总的来说。我们要更加关注render里做的事情,还有trySubscribe里的handle! 第一次流程走通之后,以后每次dispatch,我们就都会执行handle,只有执行了handle才会有后面的生命周期函数执行。


    react-redux就是将react和redux结合在一起。 react负责UI, redux负责业务逻辑,



    下面给几个图,大家结合着看吧:


    最后补充一下 connect 参数的实例:

    down voteaccepted
    function mapStateToProps(state) {
      return {
        user: state.app.user
      };
    }
    
    function mapDispatchToProps(dispatch) {
      return {
        actions: bindActionCreators(LoginActions, dispatch),
        routerActions: bindActionCreators({pushState}, dispatch)
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(LoginPage);
  • 相关阅读:
    路漫漫的自学之路(1)
    feel better about yourself 完美破解MyEclipse 5.5.1 GA 注册码呵呵
    prctise .cpp & .h 的拆分
    JsDemo
    intro
    英汉词典
    曾国藩名言
    挫折
    去掉‘为帮助保护您的安全,internet explorer已经限制此文件显示可能访问您的计算机的活动内容’提示
    曾国藩对联欣赏
  • 原文地址:https://www.cnblogs.com/huenchao/p/6095093.html
Copyright © 2020-2023  润新知