1
1
import hoistStatics from 'hoist-non-react-statics'
2
2
import invariant from 'invariant'
3
3
import { Component , createElement } from 'react'
4
+ import polyfill from 'react-lifecycles-compat'
4
5
5
6
import Subscription from '../utils/Subscription'
6
7
import { storeShape , subscriptionShape } from '../utils/PropTypes'
@@ -23,6 +24,10 @@ function makeSelectorStateful(sourceSelector, store) {
23
24
selector . shouldComponentUpdate = true
24
25
selector . error = error
25
26
}
27
+ } ,
28
+ clean : function cleanComponentSelector ( ) {
29
+ selector . run = noop
30
+ selector . shouldComponentUpdate = false
26
31
}
27
32
}
28
33
@@ -86,6 +91,11 @@ export default function connectAdvanced(
86
91
[ subscriptionKey ] : subscriptionShape ,
87
92
}
88
93
94
+ function getDerivedStateFromProps ( nextProps , prevState ) {
95
+ prevState . selector . run ( nextProps )
96
+ return null
97
+ }
98
+
89
99
return function wrapWithConnect ( WrappedComponent ) {
90
100
invariant (
91
101
typeof WrappedComponent == 'function' ,
@@ -117,7 +127,6 @@ export default function connectAdvanced(
117
127
super ( props , context )
118
128
119
129
this . version = version
120
- this . state = { }
121
130
this . renderCount = 0
122
131
this . store = props [ storeKey ] || context [ storeKey ]
123
132
this . propsMode = Boolean ( props [ storeKey ] )
@@ -129,7 +138,9 @@ export default function connectAdvanced(
129
138
`or explicitly pass "${ storeKey } " as a prop to "${ displayName } ".`
130
139
)
131
140
132
- this . initSelector ( )
141
+ this . state = {
142
+ selector : this . createSelector ( )
143
+ }
133
144
this . initSubscription ( )
134
145
}
135
146
@@ -152,24 +163,20 @@ export default function connectAdvanced(
152
163
// dispatching an action in its componentWillMount, we have to re-run the select and maybe
153
164
// re-render.
154
165
this . subscription . trySubscribe ( )
155
- this . selector . run ( this . props )
156
- if ( this . selector . shouldComponentUpdate ) this . forceUpdate ( )
166
+ this . state . selector . run ( this . props )
167
+ if ( this . state . selector . shouldComponentUpdate ) this . forceUpdate ( )
157
168
}
158
169
159
- shouldComponentUpdate ( nextProps ) {
160
- if ( nextProps !== this . props ) {
161
- this . selector . run ( nextProps )
162
- }
163
- return this . selector . shouldComponentUpdate
170
+ shouldComponentUpdate ( nextProps , nextState ) {
171
+ return nextState . selector . shouldComponentUpdate
164
172
}
165
173
166
174
componentWillUnmount ( ) {
167
175
if ( this . subscription ) this . subscription . tryUnsubscribe ( )
168
176
this . subscription = null
169
177
this . notifyNestedSubs = noop
170
178
this . store = null
171
- this . selector . run = noop
172
- this . selector . shouldComponentUpdate = false
179
+ this . state . selector . clean ( )
173
180
}
174
181
175
182
getWrappedInstance ( ) {
@@ -184,10 +191,9 @@ export default function connectAdvanced(
184
191
this . wrappedInstance = ref
185
192
}
186
193
187
- initSelector ( ) {
194
+ createSelector ( ) {
188
195
const sourceSelector = selectorFactory ( this . store . dispatch , selectorFactoryOptions )
189
- this . selector = makeSelectorStateful ( sourceSelector , this . store )
190
- this . selector . run ( this . props )
196
+ return makeSelectorStateful ( sourceSelector , this . store )
191
197
}
192
198
193
199
initSubscription ( ) {
@@ -208,9 +214,9 @@ export default function connectAdvanced(
208
214
}
209
215
210
216
onStateChange ( ) {
211
- this . selector . run ( this . props )
217
+ this . state . selector . run ( this . props )
212
218
213
- if ( ! this . selector . shouldComponentUpdate ) {
219
+ if ( ! this . state . selector . shouldComponentUpdate ) {
214
220
this . notifyNestedSubs ( )
215
221
} else {
216
222
this . componentDidUpdate = this . notifyNestedSubsOnComponentDidUpdate
@@ -246,10 +252,7 @@ export default function connectAdvanced(
246
252
}
247
253
248
254
render ( ) {
249
- const selector = this . selector
250
-
251
- // Handle forceUpdate
252
- if ( ! selector . shouldComponentUpdate ) selector . run ( this . props )
255
+ const selector = this . state . selector
253
256
254
257
selector . shouldComponentUpdate = false
255
258
@@ -266,13 +269,13 @@ export default function connectAdvanced(
266
269
Connect . childContextTypes = childContextTypes
267
270
Connect . contextTypes = contextTypes
268
271
Connect . propTypes = contextTypes
272
+ Connect . getDerivedStateFromProps = getDerivedStateFromProps
269
273
270
274
if ( process . env . NODE_ENV !== 'production' ) {
271
275
Connect . prototype . componentDidUpdate = function componentDidUpdate ( ) {
272
276
// We are hot reloading!
273
277
if ( this . version !== version ) {
274
278
this . version = version
275
- this . initSelector ( )
276
279
277
280
// If any connected descendants don't hot reload (and resubscribe in the process), their
278
281
// listeners will be lost when we unsubscribe. Unfortunately, by copying over all
@@ -291,11 +294,15 @@ export default function connectAdvanced(
291
294
oldListeners . forEach ( listener => this . subscription . listeners . subscribe ( listener ) )
292
295
}
293
296
294
- if ( this . selector . shouldComponentUpdate ) this . setState ( dummyState )
297
+ const selector = this . createSelector ( )
298
+ selector . run ( this . props )
299
+ this . setState ( { selector} )
295
300
}
296
301
}
297
302
}
298
303
304
+ polyfill ( Connect )
305
+
299
306
return hoistStatics ( Connect , WrappedComponent )
300
307
}
301
308
}
0 commit comments