19
"use strict";
20
21
var ReactComponent = require('ReactComponent');
22
+var ReactContext = require('ReactContext');
23
var ReactCurrentOwner = require('ReactCurrentOwner');
24
var ReactErrorUtils = require('ReactErrorUtils');
25
var ReactOwner = require('ReactOwner');
26
var ReactPerf = require('ReactPerf');
27
var ReactPropTransferer = require('ReactPropTransferer');
28
+var ReactPropTypeLocations = require('ReactPropTypeLocations');
29
var ReactUpdates = require('ReactUpdates');
30
31
var invariant = require('invariant');
100
*/
101
propTypes: SpecPolicy.DEFINE_MANY_MERGED,
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,
118
119
120
// ==== Definition methods ====
147
*/
148
getInitialState: SpecPolicy.DEFINE_MANY_MERGED,
149
150
+ /**
151
+ * @return {object}
152
+ * @optional
153
+ */
154
+ getChildContext: SpecPolicy.DEFINE_MANY_MERGED,
155
+
156
/**
157
* Uses props from `this.props` and state from `this.state` to render the
158
* structure of the component.
237
238
/**
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`.
242
*
243
* Use this as an opportunity to perform preparation before an update occurs.
244
*
246
*
247
* @param {object} nextProps
248
* @param {?object} nextState
249
+ * @param {?object} nextContext
250
* @param {ReactReconcileTransaction} transaction
251
* @optional
252
*/
260
*
261
* @param {object} prevProps
262
* @param {?object} prevState
263
+ * @param {?object} prevContext
264
* @param {DOMElement} rootNode DOM element representing the component.
265
* @optional
266
*/
314
}
315
}
316
},
317
+ childContextTypes: function(Constructor, childContextTypes) {
318
+ Constructor.childContextTypes = childContextTypes;
319
+ },
320
+ contextTypes: function(Constructor, contextTypes) {
321
+ Constructor.contextTypes = contextTypes;
322
+ },
323
propTypes: function(Constructor, propTypes) {
324
Constructor.propTypes = propTypes;
325
}
544
construct: function(initialProps, children) {
545
// Children can be either an array or more than one argument
546
ReactComponent.Mixin.construct.apply(this, arguments);
547
+
548
this.state = null;
549
this._pendingState = null;
550
+
551
+ this.context = this._processContext(ReactContext.current);
552
+ this._currentContext = ReactContext.current;
553
+ this._pendingContext = null;
554
+
555
this._compositeLifeCycleState = null;
556
},
557
697
ReactUpdates.enqueueUpdate(this, callback);
698
},
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
+
758
/**
759
* Processes props by setting default values for unspecified props and
760
* asserting that the props are valid.
763
* @private
764
*/
765
_processProps: function(props) {
670
- var propName;
766
var defaultProps = this._defaultProps;
672
- for (propName in defaultProps) {
767
+ for (var propName in defaultProps) {
768
if (typeof props[propName] === 'undefined') {
769
props[propName] = defaultProps[propName];
770
}
771
}
772
var propTypes = this.constructor.propTypes;
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);
792
}
793
}
794
},
814
_performUpdateIfNecessary: function(transaction) {
815
if (this._pendingProps == null &&
816
this._pendingState == null &&
817
+ this._pendingContext == null &&
818
!this._pendingForceUpdate) {
819
return;
820
}
836
var nextState = this._pendingState || this.state;
837
this._pendingState = null;
838
839
+ var nextContext = this._pendingContext || this._currentContext;
840
+ this._pendingContext = null;
841
+
842
if (this._pendingForceUpdate ||
843
!this.shouldComponentUpdate ||
733
- this.shouldComponentUpdate(nextProps, nextState)) {
844
+ this.shouldComponentUpdate(nextProps, nextState, nextContext)) {
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
+ );
853
} else {
854
// If it's determined that a component should not update, we still want
855
// to set props and state.
856
this.props = nextProps;
857
this.state = nextState;
858
+ this.context = nextContext;
859
}
860
861
this._compositeLifeCycleState = null;
867
*
868
* @param {object} nextProps Next object to set as properties.
869
* @param {?object} nextState Next object to set as state.
870
+ * @param {?object} nextContext Next object to set as context.
871
* @param {ReactReconcileTransaction} transaction
872
* @private
873
*/
756
- _performComponentUpdate: function(nextProps, nextState, transaction) {
874
+ _performComponentUpdate: function(
875
+ nextProps,
876
+ nextState,
877
+ nextContext,
878
+ transaction
879
+ ) {
880
var prevProps = this.props;
881
var prevState = this.state;
882
+ var prevContext = this.context;
883
884
if (this.componentWillUpdate) {
761
- this.componentWillUpdate(nextProps, nextState);
885
+ this.componentWillUpdate(nextProps, nextState, nextContext);
886
}
887
888
this.props = nextProps;
889
this.state = nextState;
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);
895
896
if (this.componentDidUpdate) {
897
transaction.getReactMountReady().enqueue(
898
this,
772
- this.componentDidUpdate.bind(this, prevProps, prevState)
899
+ this.componentDidUpdate.bind(this, prevProps, prevState, prevContext)
900
);
901
}
902
},
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
+
913
/**
914
* Updates the component's currently mounted DOM representation.
915
*
919
* @param {ReactReconcileTransaction} transaction
920
* @param {object} prevProps
921
* @param {?object} prevState
922
+ * @param {?object} prevContext
923
* @internal
924
* @overridable
925
*/
926
updateComponent: ReactPerf.measure(
927
'ReactCompositeComponent',
928
'updateComponent',
792
- function(transaction, prevProps, prevState) {
929
+ function(transaction, prevProps, prevState, prevContext) {
930
ReactComponent.Mixin.updateComponent.call(this, transaction, prevProps);
931
var prevComponent = this._renderedComponent;
932
var nextComponent = this._renderValidatedComponent();
988
*/
989
_renderValidatedComponent: function() {
990
var renderedComponent;
991
+ var previousContext = ReactContext.current;
992
+ ReactContext.current = this._processChildContext(this._currentContext);
993
ReactCurrentOwner.current = this;
994
try {
995
renderedComponent = this.render();
997
// IE8 requires `catch` in order to use `finally`.
998
throw error;
999
} finally {
1000
+ ReactContext.current = previousContext;
1001
ReactCurrentOwner.current = null;
1002
}
1003
invariant(