11import React from "react"
22import PropTypes from "prop-types"
3- import invariant from "invariant "
3+ import warning from "warning "
44
55function createBroadcast ( initialValue ) {
6- let subscribers = [ ]
76 let currentValue = initialValue
7+ let subscribers = [ ]
88
99 const publish = value => {
1010 currentValue = value
11- subscribers . forEach ( subscriber => subscriber ( currentValue ) )
11+ subscribers . forEach ( s => s ( currentValue ) )
1212 }
1313
1414 const subscribe = subscriber => {
1515 subscribers . push ( subscriber )
16- return ( ) => ( subscribers = subscribers . filter ( item => item !== subscriber ) )
16+
17+ return ( ) => {
18+ subscribers = subscribers . filter ( s => s !== subscriber )
19+ }
1720 }
1821
19- let broadcastInstance = null
22+ const channel = Symbol ( )
2023
2124 /**
2225 * 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.
2427 */
2528 class Broadcast extends React . Component {
2629 /**
@@ -43,16 +46,24 @@ function createBroadcast(initialValue) {
4346 */
4447 static initialValue = initialValue
4548
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+ }
5352
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+ }
5565
66+ componentDidMount ( ) {
5667 if ( this . props . value !== currentValue ) {
5768 // TODO: Publish and warn about the double render
5869 // problem if there are existing subscribers? Or
@@ -66,12 +77,6 @@ function createBroadcast(initialValue) {
6677 }
6778 }
6879
69- componentWillUnmount ( ) {
70- if ( broadcastInstance === this ) {
71- broadcastInstance = null
72- }
73- }
74-
7580 render ( ) {
7681 return this . props . children
7782 }
@@ -82,24 +87,41 @@ function createBroadcast(initialValue) {
8287 * and calls its render prop with the result.
8388 */
8489 class Subscriber extends React . Component {
90+ static contextTypes = {
91+ broadcasts : PropTypes . object
92+ }
93+
8594 static propTypes = {
86- children : PropTypes . func
95+ children : PropTypes . func ,
96+ quiet : PropTypes . bool
97+ }
98+
99+ static defaultProps = {
100+ quiet : false
87101 }
88102
89103 state = {
90104 value : currentValue
91105 }
92106
93107 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+ }
97121 }
98122
99123 componentWillUnmount ( ) {
100- if ( this . unsubscribe ) {
101- this . unsubscribe ( )
102- }
124+ if ( this . unsubscribe ) this . unsubscribe ( )
103125 }
104126
105127 render ( ) {
0 commit comments