正式學習React(五) react-redux源碼分析

磨刀不誤砍柴工,咱先把react-redux裏的工具函數分析一下:react

源碼點這裏git

 shallowEqual.jsgithub

 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是否相等。 redux

storeShape.jsapi

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.app

warning.jside

 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 }

上一篇講過 bindActionCreatorspost


它返回的這個對象直接是以 咱們定義單個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 的屬性是須要經過  指定可訪問的 元素同樣。 指定的傳遞給子組件的屬性須要先經過  來指定,否則會產生錯誤。
51 Provider.childContextTypes = { 52 store: storeShape.isRequired 53 }contextTypesgetChildContextchildContextTypes

 

 關於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 vote accepted
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);
相關文章
相關標籤/搜索