1
1
import { Component , PropTypes , createElement } from 'react'
2
2
import hoistStatics from 'hoist-non-react-statics'
3
3
import invariant from 'invariant'
4
- import result from 'lodash/result' ;
4
+ import toPath from 'lodash/toPath' ;
5
+ import isFunction from 'lodash/isFunction' ;
6
+ import isString from 'lodash/isString' ;
7
+ import memoize from 'lodash/memoize' ;
5
8
6
9
import { create } from './create'
7
10
@@ -12,39 +15,65 @@ const storeShape = PropTypes.shape({
12
15
getState : PropTypes . func . isRequired
13
16
} ) ;
14
17
15
- export function connect ( namespace ) {
18
+
19
+ const connectNamespace = memoize ( create ) ;
20
+
21
+
22
+ export function connect ( namespace , reducer ) {
23
+ invariant ( isString ( namespace ) || isFunction ( namespace ) ,
24
+ `Expected "namespace" to be of type string or function`
25
+ ) ;
26
+
16
27
return function wrapWithComponent ( WrappedComponent ) {
17
28
class Connect extends Component {
18
29
constructor ( props , context ) {
19
- super ( props , context )
30
+ super ( ...arguments ) ;
31
+
20
32
this . store = props . store || context . store
21
33
22
34
invariant ( this . store ,
23
35
`Could not find "store" in either the context or ` +
24
36
`props of "${ this . constructor . displayName } ". `
25
37
)
26
38
27
- this . childProps = create ( namespace , this . store )
39
+ this . namespace = this . getNamespace ( props , this . store )
28
40
this . state = {
29
- namespace : result ( this . store . getState ( ) , `namespace.${ namespace } ` , { } ) ,
30
- version : 0
41
+ version : this . namespace . version ( )
31
42
}
32
43
}
33
44
45
+ getNamespace ( props = this . props , store = this . store ) {
46
+ return (
47
+ connectNamespace (
48
+ [ 'namespace' ,
49
+ ...toPath (
50
+ isFunction ( namespace ) ?
51
+ namespace ( state , props ) : namespace ) ] ,
52
+ store
53
+ )
54
+ )
55
+ }
56
+
34
57
componentDidMount ( ) {
35
- this . unsubscribe = this . store . subscribe ( this . handleChange . bind ( this ) )
58
+ if ( ! this . unsubscribe ) {
59
+ this . unsubscribe =
60
+ this . store . subscribe ( this . handleChange . bind ( this ) ) ;
61
+ this . handleChange ( ) ;
62
+ }
36
63
}
37
64
38
65
componentWillUnmount ( ) {
39
- this . unsubscribe ( )
40
- this . unsubscribe = null
66
+ if ( this . unsubscribe ) {
67
+ // comma operator, because why not?
68
+ this . unsubscribe = this . unsubscribe ( ) , null ;
69
+ }
41
70
}
42
71
43
72
componentWillUpdate ( nextProps , nextState ) {
44
73
if ( nextState . version !== this . state . version ) {
45
- this . childProps = {
46
- ...this . childProps ,
47
- version : nextState . version
74
+ this . namespace = {
75
+ ...this . getNamespace ( nextProps ) ,
76
+ _version : nextState . version
48
77
}
49
78
}
50
79
}
@@ -54,19 +83,17 @@ export function connect(namespace) {
54
83
return
55
84
}
56
85
57
- const prev = this . state . namespace
58
- const next = result ( this . store . getState ( ) , `namespace.${ namespace } ` , prev )
86
+ const prev = this . state . version
87
+ const next = this . namespace . version ( ) ;
88
+
59
89
60
90
if ( prev !== next ) {
61
- this . setState ( {
62
- namespace : next ,
63
- version : this . state . version + 1
64
- } )
91
+ this . setState ( { version : next } )
65
92
}
66
93
}
67
94
68
95
render ( ) {
69
- return createElement ( WrappedComponent , { ...this . props , [ namespace ] : this . childProps } )
96
+ return createElement ( WrappedComponent , { ...this . props , [ namespace ] : this . namespace } )
70
97
}
71
98
}
72
99
0 commit comments