1
1
import hoistStatics from 'hoist-non-react-statics'
2
2
import invariant from 'invariant'
3
- import React , { Component , PureComponent } from 'react'
3
+ import React , { Component , PureComponent } from 'react'
4
4
import propTypes from 'prop-types'
5
- import { isValidElementType } from 'react-is'
5
+ import { isValidElementType } from 'react-is'
6
6
7
7
import Context from './Context'
8
8
9
9
const ReduxConsumer = Context . Consumer
10
10
11
+ const createGetter = ( source , callback ) => {
12
+ const getter = { }
13
+
14
+ Object
15
+ . getOwnPropertyNames ( source )
16
+ . forEach ( key => getter [ key ] = {
17
+ get : ( ) => callback ( key ) ,
18
+ enumerable : true
19
+ } )
20
+
21
+ return getter
22
+ } ;
23
+
11
24
export default function connectAdvanced (
12
25
/*
13
26
selectorFactory is a func that is responsible for returning the selector function used to
@@ -87,41 +100,34 @@ export default function connectAdvanced(
87
100
88
101
const displayName = getDisplayName ( wrappedComponentName )
89
102
90
- let PureWrapper
91
-
92
- if ( withRef ) {
93
- class PureWrapperRef extends Component {
94
- shouldComponentUpdate ( nextProps ) {
95
- return nextProps . derivedProps !== this . props . derivedProps
96
- }
97
103
98
- render ( ) {
99
- let { forwardRef, derivedProps } = this . props
100
- return < WrappedComponent { ...derivedProps } ref = { forwardRef } />
101
- }
102
- }
103
- PureWrapperRef . propTypes = {
104
- derivedProps : propTypes . object ,
105
- forwardRef : propTypes . oneOfType ( [
106
- propTypes . func ,
107
- propTypes . object
108
- ] )
104
+ class PureWrapper extends Component {
105
+ shouldComponentUpdate ( nextProps ) {
106
+ return nextProps . derivedProps !== this . props . derivedProps
109
107
}
110
- PureWrapper = PureWrapperRef
111
- } else {
112
- class PureWrapperNoRef extends Component {
113
- shouldComponentUpdate ( nextProps ) {
114
- return nextProps . derivedProps !== this . props . derivedProps
115
- }
116
108
117
- render ( ) {
118
- return < WrappedComponent { ...this . props . derivedProps } />
109
+ componentDidUpdate ( prevProps ) {
110
+ if ( prevProps . observedBits !== this . props . observedBits ) {
111
+ this . props . setObservedBits ( this . props . observedBits )
119
112
}
120
113
}
121
- PureWrapperNoRef . propTypes = {
122
- derivedProps : propTypes . object ,
114
+
115
+ render ( ) {
116
+ let { forwardRef, derivedProps} = this . props
117
+ return withRef
118
+ ? < WrappedComponent { ...derivedProps } ref = { forwardRef } />
119
+ : < WrappedComponent { ...derivedProps } />
123
120
}
124
- PureWrapper = PureWrapperNoRef
121
+ }
122
+
123
+ PureWrapper . propTypes = {
124
+ observedProps : propTypes . number ,
125
+ setObservedBits : propTypes . func ,
126
+ derivedProps : propTypes . object ,
127
+ forwardRef : propTypes . oneOfType ( [
128
+ propTypes . func ,
129
+ propTypes . object
130
+ ] ) ,
125
131
}
126
132
127
133
const selectorFactoryOptions = {
@@ -151,30 +157,70 @@ export default function connectAdvanced(
151
157
)
152
158
this . generatedDerivedProps = this . makeDerivedPropsGenerator ( )
153
159
this . renderWrappedComponent = this . renderWrappedComponent . bind ( this )
160
+ this . setObservedBits = this . setObservedBits . bind ( this )
161
+
162
+ this . state = {
163
+ observedBits : 0xFFFFFFFF
164
+ }
165
+ }
166
+
167
+ setObservedBits ( bits ) {
168
+ if ( ! connectOptions . observer && ( bits || ! selectorFactory ) ) {
169
+ this . setState ( state => {
170
+ if ( state . observedBits !== bits ) {
171
+ return {
172
+ observedBits : 6 | bits
173
+ }
174
+ }
175
+ return null ;
176
+ } )
177
+ }
154
178
}
155
179
156
180
makeDerivedPropsGenerator ( ) {
157
181
let lastProps
158
182
let lastState
159
183
let lastDerivedProps
160
184
let lastStore
185
+ let lastHashFunction
161
186
let sourceSelector
162
- return ( state , props , store ) => {
187
+ let stateGetter
188
+ let observedBits
189
+ return ( state , props , store , hashFunction ) => {
163
190
if ( ( connectOptions . pure && lastProps === props ) && ( lastState === state ) ) {
164
191
return lastDerivedProps
165
192
}
166
193
if ( store !== lastStore ) {
167
194
lastStore = store
168
195
sourceSelector = selectorFactory ( store . dispatch , selectorFactoryOptions )
169
196
}
197
+
198
+ if ( hashFunction !== lastHashFunction ) {
199
+ stateGetter = createGetter ( state , key => {
200
+ observedBits = hashFunction ( observedBits , key )
201
+ return lastState [ key ]
202
+ } )
203
+ lastHashFunction = hashFunction
204
+ }
170
205
lastProps = props
171
206
lastState = state
172
- const nextProps = sourceSelector ( state , props )
173
- if ( lastDerivedProps === nextProps ) {
174
- return lastDerivedProps
207
+
208
+ observedBits = 0
209
+ const couldProxyState = typeof state === 'object' && ! Array . isArray ( state )
210
+ if ( couldProxyState ) {
211
+ const stateProxy = { }
212
+ Object . defineProperties ( stateProxy , stateGetter ) ;
213
+ lastDerivedProps = sourceSelector ( stateProxy , props )
214
+ return {
215
+ derivedProps : lastDerivedProps ,
216
+ observedBits
217
+ }
218
+ }
219
+ lastDerivedProps = sourceSelector ( state , props )
220
+ return {
221
+ derivedProps : lastDerivedProps ,
222
+ observedBits : 0xFFFFFFFF
175
223
}
176
- lastDerivedProps = nextProps
177
- return lastDerivedProps
178
224
}
179
225
}
180
226
@@ -185,14 +231,21 @@ export default function connectAdvanced(
185
231
`or pass a custom React context provider to <Provider> and the corresponding ` +
186
232
`React context consumer to ${ displayName } in connect options.`
187
233
)
188
- const { state, store } = value
189
- const { forwardRef, props } = this . props
190
- let derivedProps = this . generatedDerivedProps ( state , props , store )
234
+ const { state, store, hashFunction } = value
235
+ const { forwardRef, props} = this . props
236
+ let { derivedProps, observedBits } = this . generatedDerivedProps ( state , props , store , hashFunction )
191
237
if ( connectOptions . pure ) {
192
- return < PureWrapper derivedProps = { derivedProps } forwardRef = { forwardRef } />
238
+ return (
239
+ < PureWrapper
240
+ derivedProps = { derivedProps }
241
+ observedBits = { observedBits }
242
+ setObservedBits = { this . setObservedBits }
243
+ forwardRef = { forwardRef }
244
+ />
245
+ )
193
246
}
194
247
195
- return < WrappedComponent { ...derivedProps } ref = { forwardRef } />
248
+ return < WrappedComponent { ...derivedProps } ref = { forwardRef } />
196
249
}
197
250
198
251
renderWrappedComponent ( value ) {
@@ -202,19 +255,25 @@ export default function connectAdvanced(
202
255
`or pass a custom React context provider to <Provider> and the corresponding ` +
203
256
`React context consumer to ${ displayName } in connect options.`
204
257
)
205
- const { state, store } = value
206
- let derivedProps = this . generatedDerivedProps ( state , this . props , store )
258
+ const { state, store, hashFunction } = value
259
+ let { derivedProps, observedBits } = this . generatedDerivedProps ( state , this . props , store , hashFunction )
207
260
if ( connectOptions . pure ) {
208
- return < PureWrapper derivedProps = { derivedProps } />
261
+ return (
262
+ < PureWrapper
263
+ derivedProps = { derivedProps }
264
+ observedBits = { observedBits }
265
+ setObservedBits = { this . setObservedBits }
266
+ />
267
+ )
209
268
}
210
269
211
270
return < WrappedComponent { ...derivedProps } />
212
271
}
213
272
214
273
render ( ) {
215
- if ( this . props . unstable_observedBits ) {
274
+ if ( this . state . observedBits ) {
216
275
return (
217
- < Consumer unstable_observedBits = { this . props . unstable_observedBits } >
276
+ < Consumer unstable_observedBits = { this . state . observedBits } >
218
277
{ this . renderWrappedComponent }
219
278
</ Consumer >
220
279
)
@@ -245,7 +304,7 @@ export default function connectAdvanced(
245
304
}
246
305
247
306
function forwardRef ( props , ref ) {
248
- return < Connect props = { props } forwardRef = { ref } />
307
+ return < Connect props = { props } forwardRef = { ref } />
249
308
}
250
309
251
310
const forwarded = React . forwardRef ( forwardRef )
0 commit comments