1
1
import React from "react"
2
2
import PropTypes from "prop-types"
3
- import invariant from "invariant "
3
+ import warning from "warning "
4
4
5
5
function createBroadcast ( initialValue ) {
6
- let subscribers = [ ]
7
6
let currentValue = initialValue
7
+ let subscribers = [ ]
8
8
9
9
const publish = value => {
10
10
currentValue = value
11
- subscribers . forEach ( subscriber => subscriber ( currentValue ) )
11
+ subscribers . forEach ( s => s ( currentValue ) )
12
12
}
13
13
14
14
const subscribe = subscriber => {
15
15
subscribers . push ( subscriber )
16
- return ( ) => ( subscribers = subscribers . filter ( item => item !== subscriber ) )
16
+
17
+ return ( ) => {
18
+ subscribers = subscribers . filter ( s => s !== subscriber )
19
+ }
17
20
}
18
21
19
- let broadcastInstance = null
22
+ const channel = Symbol ( )
20
23
21
24
/**
22
25
* A <Broadcast> is a container for a "value" that its <Subscriber>
23
- * may subscribe to. A <Broadcast> may only be rendered once.
26
+ * may subscribe to.
24
27
*/
25
28
class Broadcast extends React . Component {
26
29
/**
@@ -43,16 +46,24 @@ function createBroadcast(initialValue) {
43
46
*/
44
47
static initialValue = initialValue
45
48
46
- componentDidMount ( ) {
47
- invariant (
48
- broadcastInstance == null ,
49
- "You cannot render the same <Broadcast> twice! There must be only one source of truth. " +
50
- "Instead of rendering another <Broadcast>, just change the `value` prop of the one " +
51
- "you already rendered."
52
- )
49
+ static contextTypes = {
50
+ broadcasts : PropTypes . object
51
+ }
53
52
54
- broadcastInstance = this
53
+ static childContextTypes = {
54
+ broadcasts : PropTypes . object . isRequired
55
+ }
56
+
57
+ getChildContext ( ) {
58
+ return {
59
+ broadcasts : {
60
+ ...this . context . broadcasts ,
61
+ [ channel ] : true
62
+ }
63
+ }
64
+ }
55
65
66
+ componentDidMount ( ) {
56
67
if ( this . props . value !== currentValue ) {
57
68
// TODO: Publish and warn about the double render
58
69
// problem if there are existing subscribers? Or
@@ -66,12 +77,6 @@ function createBroadcast(initialValue) {
66
77
}
67
78
}
68
79
69
- componentWillUnmount ( ) {
70
- if ( broadcastInstance === this ) {
71
- broadcastInstance = null
72
- }
73
- }
74
-
75
80
render ( ) {
76
81
return this . props . children
77
82
}
@@ -82,24 +87,41 @@ function createBroadcast(initialValue) {
82
87
* and calls its render prop with the result.
83
88
*/
84
89
class Subscriber extends React . Component {
90
+ static contextTypes = {
91
+ broadcasts : PropTypes . object
92
+ }
93
+
85
94
static propTypes = {
86
- children : PropTypes . func
95
+ children : PropTypes . func ,
96
+ quiet : PropTypes . bool
97
+ }
98
+
99
+ static defaultProps = {
100
+ quiet : false
87
101
}
88
102
89
103
state = {
90
104
value : currentValue
91
105
}
92
106
93
107
componentDidMount ( ) {
94
- this . unsubscribe = subscribe ( value => {
95
- this . setState ( { value } )
96
- } )
108
+ const broadcasts = this . context . broadcasts
109
+ const inContext = broadcasts && broadcasts [ channel ]
110
+
111
+ warning (
112
+ inContext || this . props . quiet ,
113
+ "<Subscriber> was rendered outside the context of its <Broadcast>"
114
+ )
115
+
116
+ if ( inContext ) {
117
+ this . unsubscribe = subscribe ( value => {
118
+ this . setState ( { value } )
119
+ } )
120
+ }
97
121
}
98
122
99
123
componentWillUnmount ( ) {
100
- if ( this . unsubscribe ) {
101
- this . unsubscribe ( )
102
- }
124
+ if ( this . unsubscribe ) this . unsubscribe ( )
103
125
}
104
126
105
127
render ( ) {
0 commit comments