@@ -7,31 +7,28 @@ import Subscription from '../utils/Subscription'
7
7
import { storeShape , subscriptionShape } from '../utils/PropTypes'
8
8
9
9
let hotReloadingVersion = 0
10
- const dummyState = { }
11
10
function noop ( ) { }
12
- function makeSelectorStateful ( sourceSelector , store ) {
13
- // wrap the selector in an object that tracks its results between runs.
14
- const selector = {
15
- run : function runComponentSelector ( props ) {
16
- try {
17
- const nextProps = sourceSelector ( store . getState ( ) , props )
18
- if ( nextProps !== selector . props || selector . error ) {
19
- selector . shouldComponentUpdate = true
20
- selector . props = nextProps
21
- selector . error = null
11
+ function makeUpdater ( sourceSelector , store ) {
12
+ return function updater ( props , prevState ) {
13
+ try {
14
+ const nextProps = sourceSelector ( store . getState ( ) , props )
15
+ if ( nextProps !== prevState . props || prevState . error ) {
16
+ return {
17
+ shouldComponentUpdate : true ,
18
+ props : nextProps ,
19
+ error : null ,
22
20
}
23
- } catch ( error ) {
24
- selector . shouldComponentUpdate = true
25
- selector . error = error
26
21
}
27
- } ,
28
- clean : function cleanComponentSelector ( ) {
29
- selector . run = noop
30
- selector . shouldComponentUpdate = false
22
+ return {
23
+ shouldComponentUpdate : false ,
24
+ }
25
+ } catch ( error ) {
26
+ return {
27
+ shouldComponentUpdate : true ,
28
+ error,
29
+ }
31
30
}
32
31
}
33
-
34
- return selector
35
32
}
36
33
37
34
export default function connectAdvanced (
@@ -92,8 +89,7 @@ export default function connectAdvanced(
92
89
}
93
90
94
91
function getDerivedStateFromProps ( nextProps , prevState ) {
95
- prevState . selector . run ( nextProps )
96
- return null
92
+ return prevState . updater ( nextProps , prevState )
97
93
}
98
94
99
95
return function wrapWithConnect ( WrappedComponent ) {
@@ -139,7 +135,7 @@ export default function connectAdvanced(
139
135
)
140
136
141
137
this . state = {
142
- selector : this . createSelector ( )
138
+ updater : this . createUpdater ( )
143
139
}
144
140
this . initSubscription ( )
145
141
}
@@ -163,20 +159,19 @@ export default function connectAdvanced(
163
159
// dispatching an action in its componentWillMount, we have to re-run the select and maybe
164
160
// re-render.
165
161
this . subscription . trySubscribe ( )
166
- this . state . selector . run ( this . props )
167
- if ( this . state . selector . shouldComponentUpdate ) this . forceUpdate ( )
162
+ this . runUpdater ( )
168
163
}
169
164
170
- shouldComponentUpdate ( nextProps , nextState ) {
171
- return nextState . selector . shouldComponentUpdate
165
+ shouldComponentUpdate ( _ , nextState ) {
166
+ return nextState . shouldComponentUpdate
172
167
}
173
168
174
169
componentWillUnmount ( ) {
175
170
if ( this . subscription ) this . subscription . tryUnsubscribe ( )
176
171
this . subscription = null
177
172
this . notifyNestedSubs = noop
178
173
this . store = null
179
- this . state . selector . clean ( )
174
+ this . isUnmounted = true
180
175
}
181
176
182
177
getWrappedInstance ( ) {
@@ -191,9 +186,17 @@ export default function connectAdvanced(
191
186
this . wrappedInstance = ref
192
187
}
193
188
194
- createSelector ( ) {
189
+ createUpdater ( ) {
195
190
const sourceSelector = selectorFactory ( this . store . dispatch , selectorFactoryOptions )
196
- return makeSelectorStateful ( sourceSelector , this . store )
191
+ return makeUpdater ( sourceSelector , this . store )
192
+ }
193
+
194
+ runUpdater ( callback = noop ) {
195
+ if ( this . isUnmounted ) {
196
+ return
197
+ }
198
+
199
+ this . setState ( prevState => prevState . updater ( this . props , prevState ) , callback )
197
200
}
198
201
199
202
initSubscription ( ) {
@@ -214,24 +217,7 @@ export default function connectAdvanced(
214
217
}
215
218
216
219
onStateChange ( ) {
217
- this . state . selector . run ( this . props )
218
-
219
- if ( ! this . state . selector . shouldComponentUpdate ) {
220
- this . notifyNestedSubs ( )
221
- } else {
222
- this . componentDidUpdate = this . notifyNestedSubsOnComponentDidUpdate
223
- this . setState ( dummyState )
224
- }
225
- }
226
-
227
- notifyNestedSubsOnComponentDidUpdate ( ) {
228
- // `componentDidUpdate` is conditionally implemented when `onStateChange` determines it
229
- // needs to notify nested subs. Once called, it unimplements itself until further state
230
- // changes occur. Doing it this way vs having a permanent `componentDidUpdate` that does
231
- // a boolean check every time avoids an extra method call most of the time, resulting
232
- // in some perf boost.
233
- this . componentDidUpdate = undefined
234
- this . notifyNestedSubs ( )
220
+ this . runUpdater ( this . notifyNestedSubs )
235
221
}
236
222
237
223
isSubscribed ( ) {
@@ -252,14 +238,10 @@ export default function connectAdvanced(
252
238
}
253
239
254
240
render ( ) {
255
- const selector = this . state . selector
256
-
257
- selector . shouldComponentUpdate = false
258
-
259
- if ( selector . error ) {
260
- throw selector . error
241
+ if ( this . state . error ) {
242
+ throw this . state . error
261
243
} else {
262
- return createElement ( WrappedComponent , this . addExtraProps ( selector . props ) )
244
+ return createElement ( WrappedComponent , this . addExtraProps ( this . state . props ) )
263
245
}
264
246
}
265
247
}
@@ -294,9 +276,9 @@ export default function connectAdvanced(
294
276
oldListeners . forEach ( listener => this . subscription . listeners . subscribe ( listener ) )
295
277
}
296
278
297
- const selector = this . createSelector ( )
298
- selector . run ( this . props )
299
- this . setState ( { selector } )
279
+ const updater = this . createUpdater ( )
280
+ this . setState ( { updater } )
281
+ this . runUpdater ( )
300
282
}
301
283
}
302
284
}
0 commit comments