11var ReactMeteorMixin = {
2- _handleMeteorChange : function ( ) {
3- if ( this . getMeteorState !== undefined ) {
4- this . setState ( this . getMeteorState ( ) )
5- }
6- } ,
2+ componentWillMount : function ( ) {
3+ var self = this ;
4+
5+ self . _meteorStateDep = new Tracker . Dependency ( ) ;
6+ self . _meteorFirstRun = true ;
77
8- _cancelComputation : function ( ) {
9- if ( this . _meteorComputation ) {
10- this . _meteorComputation . stop ( )
11- this . _meteorComputation = null
8+ if ( Meteor . isClient ) {
9+ Tracker . autorun ( function ( computation ) {
10+ self . _meteorComputation = computation ;
11+ self . _meteorStateDep . depend ( ) ;
12+
13+ if ( self . startMeteorSubscriptions ) {
14+ // Calling this method in a Tracker.autorun callback will ensure
15+ // that the subscriptions are canceled when the computation stops.
16+ self . startMeteorSubscriptions ( ) ;
1217 }
13- } ,
1418
15- componentWillMount : function ( ) {
16- this . _meteorComputation = Tracker . autorun ( this . _handleMeteorChange )
17- } ,
19+ enqueueMeteorStateUpdate ( self ) ;
20+ } ) ;
21+
22+ } else {
23+ enqueueMeteorStateUpdate ( self ) ;
24+ }
25+ } ,
26+
27+ componentWillUpdate : function ( nextProps , nextState ) {
28+ if ( this . _meteorCalledSetState ) {
29+ // If this component update was triggered by the ReactMeteor.Mixin,
30+ // then we do not want to trigger the change event again, because
31+ // that would lead to an infinite update loop.
32+ this . _meteorCalledSetState = false ;
33+ return ;
34+ }
35+
36+ if ( this . _meteorStateDep ) {
37+ this . _meteorStateDep . changed ( ) ;
38+ }
39+ } ,
40+
41+ componentWillUnmount : function ( ) {
42+ if ( this . _meteorComputation ) {
43+ this . _meteorComputation . stop ( ) ;
44+ this . _meteorComputation = null ;
45+ }
46+ }
47+ } ;
48+
49+ function enqueueMeteorStateUpdate ( component ) {
50+ var partialState =
51+ component . getMeteorState &&
52+ component . getMeteorState ( ) ;
53+
54+ if ( ! partialState ) {
55+ // The getMeteorState method can return a falsy value to avoid
56+ // triggering a state update.
57+ return ;
58+ }
59+
60+ if ( component . _meteorFirstRun ) {
61+ // If it's the first time we've called enqueueMeteorStateUpdate since
62+ // the component was mounted, set the state synchronously.
63+ component . _meteorFirstRun = false ;
64+ component . _meteorCalledSetState = true ;
65+ component . setState ( partialState ) ;
66+ return ;
67+ }
68+
69+ Tracker . afterFlush ( function ( ) {
70+ component . _meteorCalledSetState = true ;
71+ component . setState ( partialState ) ;
72+ } ) ;
73+ }
1874
19- componentWillReceiveProps : function ( nextProps ) {
20- var oldProps = this . props
21- this . props = nextProps
22- this . _handleMeteorChange ( )
23- this . props = oldProps
24- } ,
75+ // Like React.render, but it replaces targetNode, and works even if
76+ // targetNode.parentNode has children other than targetNode.
77+ function renderInPlaceOfNode ( reactElement , targetNode ) {
78+ var container = targetNode . parentNode ;
79+ var prevSibs = [ ] ;
80+ var nextSibs = [ ] ;
81+ var sibs = prevSibs ;
82+ var child = container . firstChild ;
2583
26- componentWillUnmount : function ( ) {
27- this . _cancelComputation ( )
84+ while ( child ) {
85+ if ( child === targetNode ) {
86+ sibs = nextSibs ;
87+ } else {
88+ sibs . push ( child ) ;
2889 }
90+ var next = child . nextSibling ;
91+ container . removeChild ( child ) ;
92+ child = next ;
93+ }
94+
95+ var result = React . render ( reactElement , container ) ;
96+ var rendered = container . firstChild ;
97+
98+ if ( prevSibs . length > 0 ) {
99+ prevSibs . forEach ( function ( sib ) {
100+ container . insertBefore ( sib , rendered ) ;
101+ } ) ;
102+ }
103+
104+ if ( nextSibs . length > 0 ) {
105+ nextSibs . forEach ( function ( sib ) {
106+ container . appendChild ( sib ) ;
107+ } ) ;
108+ }
109+
110+ return result ;
29111}
30112
113+ function unmountComponent ( reactComponent ) {
114+ var rootNode = React . findDOMNode ( reactComponent ) ;
115+ var container = rootNode && rootNode . parentNode ;
116+
117+ if ( container ) {
118+ var siblings = [ ] ;
119+ var sibling = container . firstChild ;
120+
121+ while ( sibling ) {
122+ var next = sibling . nextSibling ;
123+ if ( sibling !== rootNode ) {
124+ siblings . push ( sibling ) ;
125+ container . removeChild ( sibling ) ;
126+ }
127+ sibling = next ;
128+ }
129+
130+ React . unmountComponentAtNode ( container ) ;
31131
32- if ( typeof exports === "object" ) {
33- ReactMeteor = exports
34- } else {
35- ReactMeteor = { }
132+ siblings . forEach ( function ( sib ) {
133+ container . appendChild ( sib ) ;
134+ } ) ;
135+ }
36136}
37137
38- ReactMeteor . Mixin = ReactMeteorMixin
138+ ReactMeteor = {
139+ Mixin : ReactMeteorMixin ,
140+
141+ // So you don't have to mix in ReactMeteor.Mixin explicitly.
142+ createClass : function createClass ( spec ) {
143+ spec . mixins = spec . mixins || [ ] ;
144+ spec . mixins . push ( ReactMeteorMixin ) ;
145+ var Cls = React . createClass ( spec ) ;
146+
147+ if ( Meteor . isClient &&
148+ typeof Template === "function" &&
149+ typeof spec . templateName === "string" ) {
150+ var template = new Template (
151+ spec . templateName ,
152+ function ( ) {
153+ // A placeholder HTML element that will serve as the mounting
154+ // point for the React component. May have siblings!
155+ return new HTML . SPAN ;
156+ }
157+ ) ;
158+
159+ template . onRendered ( function ( ) {
160+ this . _reactComponent = renderInPlaceOfNode (
161+ // Equivalent to <Cls {...this.data} />:
162+ React . createElement ( Cls , this . data || { } ) ,
163+ this . find ( "span" )
164+ ) ;
165+ } ) ;
166+
167+ template . onDestroyed ( function ( ) {
168+ unmountComponent ( this . _reactComponent ) ;
169+ } ) ;
170+
171+ Template [ spec . templateName ] = template ;
172+ }
173+
174+ return Cls ;
175+ }
176+ } ;
0 commit comments