磨刀不誤砍柴工,咱先把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 參數的實例:
|
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);
|