19
19
"use strict" ;
20
20
21
21
var ReactComponent = require ( 'ReactComponent' ) ;
22
+ var ReactContext = require ( 'ReactContext' ) ;
22
23
var ReactCurrentOwner = require ( 'ReactCurrentOwner' ) ;
23
24
var ReactErrorUtils = require ( 'ReactErrorUtils' ) ;
24
25
var ReactOwner = require ( 'ReactOwner' ) ;
25
26
var ReactPerf = require ( 'ReactPerf' ) ;
26
27
var ReactPropTransferer = require ( 'ReactPropTransferer' ) ;
28
+ var ReactPropTypeLocations = require ( 'ReactPropTypeLocations' ) ;
27
29
var ReactUpdates = require ( 'ReactUpdates' ) ;
28
30
29
31
var invariant = require ( 'invariant' ) ;
@@ -98,6 +100,21 @@ var ReactCompositeComponentInterface = {
98
100
*/
99
101
propTypes : SpecPolicy . DEFINE_MANY_MERGED ,
100
102
103
+ /**
104
+ * Definition of context types for this component.
105
+ *
106
+ * @type {object }
107
+ * @optional
108
+ */
109
+ contextTypes : SpecPolicy . DEFINE_MANY_MERGED ,
110
+
111
+ /**
112
+ * Definition of context types this component sets for its children.
113
+ *
114
+ * @type {object }
115
+ * @optional
116
+ */
117
+ childContextTypes : SpecPolicy . DEFINE_MANY_MERGED ,
101
118
102
119
103
120
// ==== Definition methods ====
@@ -130,6 +147,12 @@ var ReactCompositeComponentInterface = {
130
147
*/
131
148
getInitialState : SpecPolicy . DEFINE_MANY_MERGED ,
132
149
150
+ /**
151
+ * @return {object }
152
+ * @optional
153
+ */
154
+ getChildContext : SpecPolicy . DEFINE_MANY_MERGED ,
155
+
133
156
/**
134
157
* Uses props from `this.props` and state from `this.state` to render the
135
158
* structure of the component.
@@ -214,14 +237,16 @@ var ReactCompositeComponentInterface = {
214
237
215
238
/**
216
239
* Invoked when the component is about to update due to a transition from
217
- * `this.props` and `this.state` to `nextProps` and `nextState`.
240
+ * `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
241
+ * and `nextContext`.
218
242
*
219
243
* Use this as an opportunity to perform preparation before an update occurs.
220
244
*
221
245
* NOTE: You **cannot** use `this.setState()` in this method.
222
246
*
223
247
* @param {object } nextProps
224
248
* @param {?object } nextState
249
+ * @param {?object } nextContext
225
250
* @param {ReactReconcileTransaction } transaction
226
251
* @optional
227
252
*/
@@ -235,6 +260,7 @@ var ReactCompositeComponentInterface = {
235
260
*
236
261
* @param {object } prevProps
237
262
* @param {?object } prevState
263
+ * @param {?object } prevContext
238
264
* @param {DOMElement } rootNode DOM element representing the component.
239
265
* @optional
240
266
*/
@@ -288,6 +314,12 @@ var RESERVED_SPEC_KEYS = {
288
314
}
289
315
}
290
316
} ,
317
+ childContextTypes : function ( Constructor , childContextTypes ) {
318
+ Constructor . childContextTypes = childContextTypes ;
319
+ } ,
320
+ contextTypes : function ( Constructor , contextTypes ) {
321
+ Constructor . contextTypes = contextTypes ;
322
+ } ,
291
323
propTypes : function ( Constructor , propTypes ) {
292
324
Constructor . propTypes = propTypes ;
293
325
}
@@ -512,8 +544,14 @@ var ReactCompositeComponentMixin = {
512
544
construct : function ( initialProps , children ) {
513
545
// Children can be either an array or more than one argument
514
546
ReactComponent . Mixin . construct . apply ( this , arguments ) ;
547
+
515
548
this . state = null ;
516
549
this . _pendingState = null ;
550
+
551
+ this . context = this . _processContext ( ReactContext . current ) ;
552
+ this . _currentContext = ReactContext . current ;
553
+ this . _pendingContext = null ;
554
+
517
555
this . _compositeLifeCycleState = null ;
518
556
} ,
519
557
@@ -659,6 +697,64 @@ var ReactCompositeComponentMixin = {
659
697
ReactUpdates . enqueueUpdate ( this , callback ) ;
660
698
} ,
661
699
700
+ /**
701
+ * Filters the context object to only contain keys specified in
702
+ * `contextTypes`, and asserts that they are valid.
703
+ *
704
+ * @param {object } context
705
+ * @return {?object }
706
+ * @private
707
+ */
708
+ _processContext : function ( context ) {
709
+ var maskedContext = null ;
710
+ var contextTypes = this . constructor . contextTypes ;
711
+ if ( contextTypes ) {
712
+ maskedContext = { } ;
713
+ for ( var contextName in contextTypes ) {
714
+ maskedContext [ contextName ] = context [ contextName ] ;
715
+ }
716
+ this . _checkPropTypes (
717
+ contextTypes ,
718
+ maskedContext ,
719
+ ReactPropTypeLocations . context
720
+ ) ;
721
+ }
722
+ return maskedContext ;
723
+ } ,
724
+
725
+ /**
726
+ * @param {object } currentContext
727
+ * @return {object }
728
+ * @private
729
+ */
730
+ _processChildContext : function ( currentContext ) {
731
+ var childContext = this . getChildContext && this . getChildContext ( ) ;
732
+ var displayName = this . constructor . displayName || 'ReactCompositeComponent' ;
733
+ if ( childContext ) {
734
+ invariant (
735
+ typeof this . constructor . childContextTypes === 'object' ,
736
+ '%s.getChildContext(): childContextTypes must be defined in order to ' +
737
+ 'use getChildContext().' ,
738
+ displayName
739
+ ) ;
740
+ this . _checkPropTypes (
741
+ this . constructor . childContextTypes ,
742
+ childContext ,
743
+ ReactPropTypeLocations . childContext
744
+ ) ;
745
+ for ( var name in childContext ) {
746
+ invariant (
747
+ name in this . constructor . childContextTypes ,
748
+ '%s.getChildContext(): key "%s" is not defined in childContextTypes.' ,
749
+ displayName ,
750
+ name
751
+ ) ;
752
+ }
753
+ return merge ( currentContext , childContext ) ;
754
+ }
755
+ return currentContext ;
756
+ } ,
757
+
662
758
/**
663
759
* Processes props by setting default values for unspecified props and
664
760
* asserting that the props are valid.
@@ -667,21 +763,32 @@ var ReactCompositeComponentMixin = {
667
763
* @private
668
764
*/
669
765
_processProps : function ( props ) {
670
- var propName ;
671
766
var defaultProps = this . _defaultProps ;
672
- for ( propName in defaultProps ) {
767
+ for ( var propName in defaultProps ) {
673
768
if ( typeof props [ propName ] === 'undefined' ) {
674
769
props [ propName ] = defaultProps [ propName ] ;
675
770
}
676
771
}
677
772
var propTypes = this . constructor . propTypes ;
678
773
if ( propTypes ) {
679
- var componentName = this . constructor . displayName ;
680
- for ( propName in propTypes ) {
681
- var checkProp = propTypes [ propName ] ;
682
- if ( checkProp ) {
683
- checkProp ( props , propName , componentName ) ;
684
- }
774
+ this . _checkPropTypes ( propTypes , props , ReactPropTypeLocations . prop ) ;
775
+ }
776
+ } ,
777
+
778
+ /**
779
+ * Assert that the props are valid
780
+ *
781
+ * @param {object } propTypes Map of prop name to a ReactPropType
782
+ * @param {object } props
783
+ * @param {string } location e.g. "prop", "context", "child context"
784
+ * @private
785
+ */
786
+ _checkPropTypes : function ( propTypes , props , location ) {
787
+ var componentName = this . constructor . displayName ;
788
+ for ( var propName in propTypes ) {
789
+ var checkProp = propTypes [ propName ] ;
790
+ if ( checkProp ) {
791
+ checkProp ( props , propName , componentName , location ) ;
685
792
}
686
793
}
687
794
} ,
@@ -707,6 +814,7 @@ var ReactCompositeComponentMixin = {
707
814
_performUpdateIfNecessary : function ( transaction ) {
708
815
if ( this . _pendingProps == null &&
709
816
this . _pendingState == null &&
817
+ this . _pendingContext == null &&
710
818
! this . _pendingForceUpdate ) {
711
819
return ;
712
820
}
@@ -728,17 +836,26 @@ var ReactCompositeComponentMixin = {
728
836
var nextState = this . _pendingState || this . state ;
729
837
this . _pendingState = null ;
730
838
839
+ var nextContext = this . _pendingContext || this . _currentContext ;
840
+ this . _pendingContext = null ;
841
+
731
842
if ( this . _pendingForceUpdate ||
732
843
! this . shouldComponentUpdate ||
733
- this . shouldComponentUpdate ( nextProps , nextState ) ) {
844
+ this . shouldComponentUpdate ( nextProps , nextState , nextContext ) ) {
734
845
this . _pendingForceUpdate = false ;
735
- // Will set `this.props` and `this.state`.
736
- this . _performComponentUpdate ( nextProps , nextState , transaction ) ;
846
+ // Will set `this.props`, `this.state` and `this.context`.
847
+ this . _performComponentUpdate (
848
+ nextProps ,
849
+ nextState ,
850
+ nextContext ,
851
+ transaction
852
+ ) ;
737
853
} else {
738
854
// If it's determined that a component should not update, we still want
739
855
// to set props and state.
740
856
this . props = nextProps ;
741
857
this . state = nextState ;
858
+ this . context = nextContext ;
742
859
}
743
860
744
861
this . _compositeLifeCycleState = null ;
@@ -750,30 +867,49 @@ var ReactCompositeComponentMixin = {
750
867
*
751
868
* @param {object } nextProps Next object to set as properties.
752
869
* @param {?object } nextState Next object to set as state.
870
+ * @param {?object } nextContext Next object to set as context.
753
871
* @param {ReactReconcileTransaction } transaction
754
872
* @private
755
873
*/
756
- _performComponentUpdate : function ( nextProps , nextState , transaction ) {
874
+ _performComponentUpdate : function (
875
+ nextProps ,
876
+ nextState ,
877
+ nextContext ,
878
+ transaction
879
+ ) {
757
880
var prevProps = this . props ;
758
881
var prevState = this . state ;
882
+ var prevContext = this . context ;
759
883
760
884
if ( this . componentWillUpdate ) {
761
- this . componentWillUpdate ( nextProps , nextState ) ;
885
+ this . componentWillUpdate ( nextProps , nextState , nextContext ) ;
762
886
}
763
887
764
888
this . props = nextProps ;
765
889
this . state = nextState ;
766
890
767
- this . updateComponent ( transaction , prevProps , prevState ) ;
891
+ this . _currentContext = nextContext ;
892
+ this . context = this . _processContext ( nextContext ) ;
893
+
894
+ this . updateComponent ( transaction , prevProps , prevState , prevContext ) ;
768
895
769
896
if ( this . componentDidUpdate ) {
770
897
transaction . getReactMountReady ( ) . enqueue (
771
898
this ,
772
- this . componentDidUpdate . bind ( this , prevProps , prevState )
899
+ this . componentDidUpdate . bind ( this , prevProps , prevState , prevContext )
773
900
) ;
774
901
}
775
902
} ,
776
903
904
+ receiveComponent : function ( nextComponent , transaction ) {
905
+ this . _pendingContext = nextComponent . _currentContext ;
906
+ ReactComponent . Mixin . receiveComponent . call (
907
+ this ,
908
+ nextComponent ,
909
+ transaction
910
+ ) ;
911
+ } ,
912
+
777
913
/**
778
914
* Updates the component's currently mounted DOM representation.
779
915
*
@@ -783,13 +919,14 @@ var ReactCompositeComponentMixin = {
783
919
* @param {ReactReconcileTransaction } transaction
784
920
* @param {object } prevProps
785
921
* @param {?object } prevState
922
+ * @param {?object } prevContext
786
923
* @internal
787
924
* @overridable
788
925
*/
789
926
updateComponent : ReactPerf . measure (
790
927
'ReactCompositeComponent' ,
791
928
'updateComponent' ,
792
- function ( transaction , prevProps , prevState ) {
929
+ function ( transaction , prevProps , prevState , prevContext ) {
793
930
ReactComponent . Mixin . updateComponent . call ( this , transaction , prevProps ) ;
794
931
var prevComponent = this . _renderedComponent ;
795
932
var nextComponent = this . _renderValidatedComponent ( ) ;
@@ -851,13 +988,16 @@ var ReactCompositeComponentMixin = {
851
988
*/
852
989
_renderValidatedComponent : function ( ) {
853
990
var renderedComponent ;
991
+ var previousContext = ReactContext . current ;
992
+ ReactContext . current = this . _processChildContext ( this . _currentContext ) ;
854
993
ReactCurrentOwner . current = this ;
855
994
try {
856
995
renderedComponent = this . render ( ) ;
857
996
} catch ( error ) {
858
997
// IE8 requires `catch` in order to use `finally`.
859
998
throw error ;
860
999
} finally {
1000
+ ReactContext . current = previousContext ;
861
1001
ReactCurrentOwner . current = null ;
862
1002
}
863
1003
invariant (
0 commit comments