@@ -73,8 +73,22 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
73
73
}
74
74
75
75
class Connect extends Component {
76
- shouldComponentUpdate ( ) {
77
- return ! pure || this . haveOwnPropsChanged || this . hasStoreStateChanged
76
+
77
+ shouldComponentUpdate ( nextProps , nextState ) {
78
+
79
+ if ( this . skipNextRender ) {
80
+ this . skipNextRender = false ;
81
+ return false ;
82
+ }
83
+
84
+ /*
85
+ console.log("shouldComponentUpdate",!pure || nextState.mergedPropsUpdated)
86
+ console.log("shouldComponentUpdate",!pure || nextState.mergedPropsUpdated)
87
+ console.log("shouldComponentUpdate",nextState)
88
+ console.log("shouldComponentUpdate nextState.mergedPropsUpdated",nextState.mergedPropsUpdated)
89
+ */
90
+ console . log ( "################## shouldComponentUpdate" , nextState . mergedPropsUpdated )
91
+ return ! pure || nextState . mergedPropsUpdated
78
92
}
79
93
80
94
constructor ( props , context ) {
@@ -88,9 +102,7 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
88
102
`Either wrap the root component in a <Provider>, ` +
89
103
`or explicitly pass "store" as a prop to "${ connectDisplayName } ".`
90
104
)
91
-
92
- const storeState = this . store . getState ( )
93
- this . state = { storeState }
105
+ this . state = { }
94
106
this . clearCache ( )
95
107
}
96
108
@@ -152,45 +164,21 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
152
164
return dispatchProps
153
165
}
154
166
155
- updateStatePropsIfNeeded ( ) {
156
- const nextStateProps = this . computeStateProps ( this . props )
157
- if ( this . stateProps && shallowEqual ( nextStateProps , this . stateProps ) ) {
158
- return false
159
- }
160
-
161
- this . stateProps = nextStateProps
162
- return true
163
- }
164
-
165
- updateDispatchPropsIfNeeded ( ) {
166
- const nextDispatchProps = this . computeDispatchProps ( this . props )
167
- if ( this . dispatchProps && shallowEqual ( nextDispatchProps , this . dispatchProps ) ) {
168
- return false
169
- }
170
-
171
- this . dispatchProps = nextDispatchProps
172
- return true
173
- }
174
-
175
- updateMergedPropsIfNeeded ( ) {
176
- const nextMergedProps = computeMergedProps ( this . stateProps , this . dispatchProps , this . props )
177
- if ( this . mergedProps && checkMergedEquals && shallowEqual ( nextMergedProps , this . mergedProps ) ) {
178
- return false
179
- }
180
-
181
- this . mergedProps = nextMergedProps
182
- return true
183
- }
184
-
185
167
isSubscribed ( ) {
186
168
return typeof this . unsubscribe === 'function'
187
169
}
188
170
189
171
trySubscribe ( ) {
172
+ console . log ( "trySubscribe" )
190
173
if ( shouldSubscribe && ! this . unsubscribe ) {
191
- this . unsubscribe = this . store . subscribe ( this . handleChange . bind ( this ) )
192
- this . handleChange ( )
174
+ this . unsubscribe = this . store . subscribe ( ( ) => {
175
+ if ( ! this . unsubscribe ) {
176
+ return
177
+ }
178
+ this . handleChange ( false , false , true )
179
+ } )
193
180
}
181
+ this . handleChange ( true , false , false )
194
182
}
195
183
196
184
tryUnsubscribe ( ) {
@@ -200,13 +188,22 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
200
188
}
201
189
}
202
190
203
- componentDidMount ( ) {
191
+ componentWillMount ( ) {
204
192
this . trySubscribe ( )
205
193
}
206
194
207
195
componentWillReceiveProps ( nextProps ) {
208
- if ( ! pure || ! shallowEqual ( nextProps , this . props ) ) {
209
- this . haveOwnPropsChanged = true
196
+ if ( pure ) {
197
+ const propsUpdated = ! shallowEqual ( nextProps , this . props )
198
+ if ( propsUpdated ) {
199
+ this . handleChange ( false , true , false )
200
+ }
201
+ else {
202
+ this . skipNextRender = true ;
203
+ }
204
+ }
205
+ else {
206
+ this . handleChange ( false , true , false )
210
207
}
211
208
}
212
209
@@ -216,120 +213,111 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
216
213
}
217
214
218
215
clearCache ( ) {
219
- this . dispatchProps = null
220
- this . stateProps = null
221
- this . mergedProps = null
222
- this . haveOwnPropsChanged = true
223
- this . hasStoreStateChanged = true
224
- this . haveStatePropsBeenPrecalculated = false
225
- this . statePropsPrecalculationError = null
226
- this . renderedElement = null
227
216
this . finalMapDispatchToProps = null
228
217
this . finalMapStateToProps = null
229
218
}
230
219
231
- handleChange ( ) {
232
- if ( ! this . unsubscribe ) {
233
- return
220
+ handleChange ( isInit , isPropsChange , isStateChange ) {
221
+ const result = tryCatch ( ( ) => this . doHandleChange ( isInit , isPropsChange , isStateChange ) )
222
+ if ( result === errorObject ) {
223
+ this . setState ( { error : errorObject . value } )
234
224
}
225
+ }
226
+
227
+ // TODO can we have a better method signature? it seems spread does not work?
228
+ doHandleChange ( isInit , isPropsChange , isStateChange ) {
229
+ console . log ( "################## handleChange for " + ( isInit ? "init" : isPropsChange ? "props" : "state" ) )
235
230
236
231
const storeState = this . store . getState ( )
237
- const prevStoreState = this . state . storeState
238
- if ( pure && prevStoreState === storeState ) {
232
+ if ( isStateChange && pure && ( storeState === this . state . storeState ) ) {
233
+ console . log ( "handleChange same state: return" )
239
234
return
240
235
}
241
236
242
- if ( pure && ! this . doStatePropsDependOnOwnProps ) {
243
- const haveStatePropsChanged = tryCatch ( this . updateStatePropsIfNeeded , this )
244
- if ( ! haveStatePropsChanged ) {
245
- return
237
+ // This is put outside because it's used in both the fast and normal paths
238
+ let stateProps
239
+ let statePropsUpdated
240
+ const setStatePropsIfNeeded = ( props ) => {
241
+ if ( typeof statePropsUpdated === 'undefined' ) {
242
+ stateProps = ( ! isInit && pure && isPropsChange && ! this . doStatePropsDependOnOwnProps ) ?
243
+ this . state . stateProps :
244
+ this . computeStateProps ( props )
245
+ statePropsUpdated = ! this . state . stateProps || ! shallowEqual ( stateProps , this . state . stateProps )
246
+ console . log ( "stateProps" , stateProps )
247
+ console . log ( "statePropsUpdated" , statePropsUpdated )
246
248
}
247
- if ( haveStatePropsChanged === errorObject ) {
248
- this . statePropsPrecalculationError = errorObject . value
249
+ }
250
+
251
+
252
+ // Fast track: bailout early because we don't need access to fresh props here
253
+ if ( isStateChange && pure && ! this . doStatePropsDependOnOwnProps ) {
254
+ setStatePropsIfNeeded ( this . props )
255
+ if ( ! statePropsUpdated ) {
256
+ console . log ( "-> fast track return" )
257
+ return
249
258
}
250
- this . haveStatePropsBeenPrecalculated = true
251
259
}
252
260
253
- this . hasStoreStateChanged = true
254
- this . setState ( { storeState } )
261
+
262
+ // Normal track: we precompute everything for shouldComponentUpdate, and try to reuse already done computation
263
+ this . setState ( ( previousState , currentProps ) => {
264
+ console . log ( "-> normal track with props=" , currentProps )
265
+
266
+ setStatePropsIfNeeded ( currentProps ) ;
267
+ const dispatchProps = ( ! isInit && pure && isPropsChange && ! this . doDispatchPropsDependOnOwnProps ) ?
268
+ this . state . dispatchProps :
269
+ this . computeDispatchProps ( currentProps )
270
+ const dispatchPropsUpdated = ! this . state . dispatchProps || ! shallowEqual ( dispatchProps , this . state . dispatchProps )
271
+ console . log ( "dispatchProps" , dispatchProps )
272
+ console . log ( "dispatchPropsUpdated" , dispatchPropsUpdated )
273
+
274
+ const mergedProps = ( statePropsUpdated || dispatchPropsUpdated || isPropsChange ) ?
275
+ computeMergedProps ( stateProps , dispatchProps , currentProps ) :
276
+ this . state . mergedProps
277
+ const mergedPropsUpdated = ! this . state . mergedProps || ! shallowEqual ( mergedProps , this . state . mergedProps )
278
+ console . log ( "mergedProps" , mergedProps )
279
+ console . log ( "mergedPropsUpdated" , mergedPropsUpdated )
280
+
281
+
282
+ return {
283
+ error : undefined ,
284
+ storeState,
285
+ stateProps,
286
+ dispatchProps,
287
+ mergedProps,
288
+ mergedPropsUpdated
289
+ }
290
+ } ) ;
255
291
}
256
292
257
293
getWrappedInstance ( ) {
258
294
invariant ( withRef ,
259
295
`To access the wrapped instance, you need to specify ` +
260
296
`{ withRef: true } as the fourth argument of the connect() call.`
261
297
)
262
-
263
298
return this . refs . wrappedInstance
264
299
}
265
300
266
- render ( ) {
267
- const {
268
- haveOwnPropsChanged,
269
- hasStoreStateChanged,
270
- haveStatePropsBeenPrecalculated,
271
- statePropsPrecalculationError,
272
- renderedElement
273
- } = this
274
-
275
- this . haveOwnPropsChanged = false
276
- this . hasStoreStateChanged = false
277
- this . haveStatePropsBeenPrecalculated = false
278
- this . statePropsPrecalculationError = null
279
-
280
- if ( statePropsPrecalculationError ) {
281
- throw statePropsPrecalculationError
282
- }
283
-
284
- let shouldUpdateStateProps = true
285
- let shouldUpdateDispatchProps = true
286
- if ( pure && renderedElement ) {
287
- shouldUpdateStateProps = hasStoreStateChanged || (
288
- haveOwnPropsChanged && this . doStatePropsDependOnOwnProps
289
- )
290
- shouldUpdateDispatchProps =
291
- haveOwnPropsChanged && this . doDispatchPropsDependOnOwnProps
292
- }
293
-
294
- let haveStatePropsChanged = false
295
- let haveDispatchPropsChanged = false
296
- if ( haveStatePropsBeenPrecalculated ) {
297
- haveStatePropsChanged = true
298
- } else if ( shouldUpdateStateProps ) {
299
- haveStatePropsChanged = this . updateStatePropsIfNeeded ( )
300
- }
301
- if ( shouldUpdateDispatchProps ) {
302
- haveDispatchPropsChanged = this . updateDispatchPropsIfNeeded ( )
303
- }
304
-
305
- let haveMergedPropsChanged = true
306
- if (
307
- haveStatePropsChanged ||
308
- haveDispatchPropsChanged ||
309
- haveOwnPropsChanged
310
- ) {
311
- haveMergedPropsChanged = this . updateMergedPropsIfNeeded ( )
312
- } else {
313
- haveMergedPropsChanged = false
301
+ getRenderedElementProps ( ) {
302
+ if ( withRef ) {
303
+ return {
304
+ ...this . state . mergedProps ,
305
+ ref : 'wrappedInstance'
306
+ }
314
307
}
315
-
316
- if ( ! haveMergedPropsChanged && renderedElement ) {
317
- return renderedElement
308
+ else {
309
+ return this . state . mergedProps
318
310
}
311
+ }
319
312
320
- if ( withRef ) {
321
- this . renderedElement = createElement ( WrappedComponent , {
322
- ...this . mergedProps ,
323
- ref : 'wrappedInstance'
324
- } )
325
- } else {
326
- this . renderedElement = createElement ( WrappedComponent ,
327
- this . mergedProps
328
- )
313
+ render ( ) {
314
+ console . log ( "################## render" )
315
+ if ( this . state . error ) {
316
+ throw this . state . error ;
329
317
}
330
-
331
- return this . renderedElement
318
+ return createElement ( WrappedComponent , this . getRenderedElementProps ( ) )
332
319
}
320
+
333
321
}
334
322
335
323
Connect . displayName = connectDisplayName
@@ -349,8 +337,10 @@ export default function connect(mapStateToProps, mapDispatchToProps, mergeProps,
349
337
350
338
// We are hot reloading!
351
339
this . version = version
352
- this . trySubscribe ( )
353
340
this . clearCache ( )
341
+ this . setState ( { } , ( ) => {
342
+ this . trySubscribe ( )
343
+ } )
354
344
}
355
345
}
356
346
0 commit comments