react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / core / ReactCompositeComponent.js
81152 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactCompositeComponent9*/1011"use strict";1213var ReactComponent = require('ReactComponent');14var ReactContext = require('ReactContext');15var ReactCurrentOwner = require('ReactCurrentOwner');16var ReactElement = require('ReactElement');17var ReactElementValidator = require('ReactElementValidator');18var ReactEmptyComponent = require('ReactEmptyComponent');19var ReactErrorUtils = require('ReactErrorUtils');20var ReactLegacyElement = require('ReactLegacyElement');21var ReactOwner = require('ReactOwner');22var ReactPerf = require('ReactPerf');23var ReactPropTransferer = require('ReactPropTransferer');24var ReactPropTypeLocations = require('ReactPropTypeLocations');25var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');26var ReactUpdates = require('ReactUpdates');2728var assign = require('Object.assign');29var instantiateReactComponent = require('instantiateReactComponent');30var invariant = require('invariant');31var keyMirror = require('keyMirror');32var keyOf = require('keyOf');33var monitorCodeUse = require('monitorCodeUse');34var mapObject = require('mapObject');35var shouldUpdateReactComponent = require('shouldUpdateReactComponent');36var warning = require('warning');3738var MIXINS_KEY = keyOf({mixins: null});3940/**41* Policies that describe methods in `ReactCompositeComponentInterface`.42*/43var SpecPolicy = keyMirror({44/**45* These methods may be defined only once by the class specification or mixin.46*/47DEFINE_ONCE: null,48/**49* These methods may be defined by both the class specification and mixins.50* Subsequent definitions will be chained. These methods must return void.51*/52DEFINE_MANY: null,53/**54* These methods are overriding the base ReactCompositeComponent class.55*/56OVERRIDE_BASE: null,57/**58* These methods are similar to DEFINE_MANY, except we assume they return59* objects. We try to merge the keys of the return values of all the mixed in60* functions. If there is a key conflict we throw.61*/62DEFINE_MANY_MERGED: null63});646566var injectedMixins = [];6768/**69* Composite components are higher-level components that compose other composite70* or native components.71*72* To create a new type of `ReactCompositeComponent`, pass a specification of73* your new class to `React.createClass`. The only requirement of your class74* specification is that you implement a `render` method.75*76* var MyComponent = React.createClass({77* render: function() {78* return <div>Hello World</div>;79* }80* });81*82* The class specification supports a specific protocol of methods that have83* special meaning (e.g. `render`). See `ReactCompositeComponentInterface` for84* more the comprehensive protocol. Any other properties and methods in the85* class specification will available on the prototype.86*87* @interface ReactCompositeComponentInterface88* @internal89*/90var ReactCompositeComponentInterface = {9192/**93* An array of Mixin objects to include when defining your component.94*95* @type {array}96* @optional97*/98mixins: SpecPolicy.DEFINE_MANY,99100/**101* An object containing properties and methods that should be defined on102* the component's constructor instead of its prototype (static methods).103*104* @type {object}105* @optional106*/107statics: SpecPolicy.DEFINE_MANY,108109/**110* Definition of prop types for this component.111*112* @type {object}113* @optional114*/115propTypes: SpecPolicy.DEFINE_MANY,116117/**118* Definition of context types for this component.119*120* @type {object}121* @optional122*/123contextTypes: SpecPolicy.DEFINE_MANY,124125/**126* Definition of context types this component sets for its children.127*128* @type {object}129* @optional130*/131childContextTypes: SpecPolicy.DEFINE_MANY,132133// ==== Definition methods ====134135/**136* Invoked when the component is mounted. Values in the mapping will be set on137* `this.props` if that prop is not specified (i.e. using an `in` check).138*139* This method is invoked before `getInitialState` and therefore cannot rely140* on `this.state` or use `this.setState`.141*142* @return {object}143* @optional144*/145getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED,146147/**148* Invoked once before the component is mounted. The return value will be used149* as the initial value of `this.state`.150*151* getInitialState: function() {152* return {153* isOn: false,154* fooBaz: new BazFoo()155* }156* }157*158* @return {object}159* @optional160*/161getInitialState: SpecPolicy.DEFINE_MANY_MERGED,162163/**164* @return {object}165* @optional166*/167getChildContext: SpecPolicy.DEFINE_MANY_MERGED,168169/**170* Uses props from `this.props` and state from `this.state` to render the171* structure of the component.172*173* No guarantees are made about when or how often this method is invoked, so174* it must not have side effects.175*176* render: function() {177* var name = this.props.name;178* return <div>Hello, {name}!</div>;179* }180*181* @return {ReactComponent}182* @nosideeffects183* @required184*/185render: SpecPolicy.DEFINE_ONCE,186187188189// ==== Delegate methods ====190191/**192* Invoked when the component is initially created and about to be mounted.193* This may have side effects, but any external subscriptions or data created194* by this method must be cleaned up in `componentWillUnmount`.195*196* @optional197*/198componentWillMount: SpecPolicy.DEFINE_MANY,199200/**201* Invoked when the component has been mounted and has a DOM representation.202* However, there is no guarantee that the DOM node is in the document.203*204* Use this as an opportunity to operate on the DOM when the component has205* been mounted (initialized and rendered) for the first time.206*207* @param {DOMElement} rootNode DOM element representing the component.208* @optional209*/210componentDidMount: SpecPolicy.DEFINE_MANY,211212/**213* Invoked before the component receives new props.214*215* Use this as an opportunity to react to a prop transition by updating the216* state using `this.setState`. Current props are accessed via `this.props`.217*218* componentWillReceiveProps: function(nextProps, nextContext) {219* this.setState({220* likesIncreasing: nextProps.likeCount > this.props.likeCount221* });222* }223*224* NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop225* transition may cause a state change, but the opposite is not true. If you226* need it, you are probably looking for `componentWillUpdate`.227*228* @param {object} nextProps229* @optional230*/231componentWillReceiveProps: SpecPolicy.DEFINE_MANY,232233/**234* Invoked while deciding if the component should be updated as a result of235* receiving new props, state and/or context.236*237* Use this as an opportunity to `return false` when you're certain that the238* transition to the new props/state/context will not require a component239* update.240*241* shouldComponentUpdate: function(nextProps, nextState, nextContext) {242* return !equal(nextProps, this.props) ||243* !equal(nextState, this.state) ||244* !equal(nextContext, this.context);245* }246*247* @param {object} nextProps248* @param {?object} nextState249* @param {?object} nextContext250* @return {boolean} True if the component should update.251* @optional252*/253shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,254255/**256* Invoked when the component is about to update due to a transition from257* `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`258* and `nextContext`.259*260* Use this as an opportunity to perform preparation before an update occurs.261*262* NOTE: You **cannot** use `this.setState()` in this method.263*264* @param {object} nextProps265* @param {?object} nextState266* @param {?object} nextContext267* @param {ReactReconcileTransaction} transaction268* @optional269*/270componentWillUpdate: SpecPolicy.DEFINE_MANY,271272/**273* Invoked when the component's DOM representation has been updated.274*275* Use this as an opportunity to operate on the DOM when the component has276* been updated.277*278* @param {object} prevProps279* @param {?object} prevState280* @param {?object} prevContext281* @param {DOMElement} rootNode DOM element representing the component.282* @optional283*/284componentDidUpdate: SpecPolicy.DEFINE_MANY,285286/**287* Invoked when the component is about to be removed from its parent and have288* its DOM representation destroyed.289*290* Use this as an opportunity to deallocate any external resources.291*292* NOTE: There is no `componentDidUnmount` since your component will have been293* destroyed by that point.294*295* @optional296*/297componentWillUnmount: SpecPolicy.DEFINE_MANY,298299300301// ==== Advanced methods ====302303/**304* Updates the component's currently mounted DOM representation.305*306* By default, this implements React's rendering and reconciliation algorithm.307* Sophisticated clients may wish to override this.308*309* @param {ReactReconcileTransaction} transaction310* @internal311* @overridable312*/313updateComponent: SpecPolicy.OVERRIDE_BASE314315};316317/**318* Mapping from class specification keys to special processing functions.319*320* Although these are declared like instance properties in the specification321* when defining classes using `React.createClass`, they are actually static322* and are accessible on the constructor instead of the prototype. Despite323* being static, they must be defined outside of the "statics" key under324* which all other static methods are defined.325*/326var RESERVED_SPEC_KEYS = {327displayName: function(Constructor, displayName) {328Constructor.displayName = displayName;329},330mixins: function(Constructor, mixins) {331if (mixins) {332for (var i = 0; i < mixins.length; i++) {333mixSpecIntoComponent(Constructor, mixins[i]);334}335}336},337childContextTypes: function(Constructor, childContextTypes) {338validateTypeDef(339Constructor,340childContextTypes,341ReactPropTypeLocations.childContext342);343Constructor.childContextTypes = assign(344{},345Constructor.childContextTypes,346childContextTypes347);348},349contextTypes: function(Constructor, contextTypes) {350validateTypeDef(351Constructor,352contextTypes,353ReactPropTypeLocations.context354);355Constructor.contextTypes = assign(356{},357Constructor.contextTypes,358contextTypes359);360},361/**362* Special case getDefaultProps which should move into statics but requires363* automatic merging.364*/365getDefaultProps: function(Constructor, getDefaultProps) {366if (Constructor.getDefaultProps) {367Constructor.getDefaultProps = createMergedResultFunction(368Constructor.getDefaultProps,369getDefaultProps370);371} else {372Constructor.getDefaultProps = getDefaultProps;373}374},375propTypes: function(Constructor, propTypes) {376validateTypeDef(377Constructor,378propTypes,379ReactPropTypeLocations.prop380);381Constructor.propTypes = assign(382{},383Constructor.propTypes,384propTypes385);386},387statics: function(Constructor, statics) {388mixStaticSpecIntoComponent(Constructor, statics);389}390};391392function getDeclarationErrorAddendum(component) {393var owner = component._owner || null;394if (owner && owner.constructor && owner.constructor.displayName) {395return ' Check the render method of `' + owner.constructor.displayName +396'`.';397}398return '';399}400401function validateTypeDef(Constructor, typeDef, location) {402for (var propName in typeDef) {403if (typeDef.hasOwnProperty(propName)) {404invariant(405typeof typeDef[propName] == 'function',406'%s: %s type `%s` is invalid; it must be a function, usually from ' +407'React.PropTypes.',408Constructor.displayName || 'ReactCompositeComponent',409ReactPropTypeLocationNames[location],410propName411);412}413}414}415416function validateMethodOverride(proto, name) {417var specPolicy = ReactCompositeComponentInterface.hasOwnProperty(name) ?418ReactCompositeComponentInterface[name] :419null;420421// Disallow overriding of base class methods unless explicitly allowed.422if (ReactCompositeComponentMixin.hasOwnProperty(name)) {423invariant(424specPolicy === SpecPolicy.OVERRIDE_BASE,425'ReactCompositeComponentInterface: You are attempting to override ' +426'`%s` from your class specification. Ensure that your method names ' +427'do not overlap with React methods.',428name429);430}431432// Disallow defining methods more than once unless explicitly allowed.433if (proto.hasOwnProperty(name)) {434invariant(435specPolicy === SpecPolicy.DEFINE_MANY ||436specPolicy === SpecPolicy.DEFINE_MANY_MERGED,437'ReactCompositeComponentInterface: You are attempting to define ' +438'`%s` on your component more than once. This conflict may be due ' +439'to a mixin.',440name441);442}443}444445function validateLifeCycleOnReplaceState(instance) {446var compositeLifeCycleState = instance._compositeLifeCycleState;447invariant(448instance.isMounted() ||449compositeLifeCycleState === CompositeLifeCycle.MOUNTING,450'replaceState(...): Can only update a mounted or mounting component.'451);452invariant(453ReactCurrentOwner.current == null,454'replaceState(...): Cannot update during an existing state transition ' +455'(such as within `render`). Render methods should be a pure function ' +456'of props and state.'457);458invariant(compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,459'replaceState(...): Cannot update while unmounting component. This ' +460'usually means you called setState() on an unmounted component.'461);462}463464/**465* Mixin helper which handles policy validation and reserved466* specification keys when building `ReactCompositeComponent` classses.467*/468function mixSpecIntoComponent(Constructor, spec) {469if (!spec) {470return;471}472473invariant(474!ReactLegacyElement.isValidFactory(spec),475'ReactCompositeComponent: You\'re attempting to ' +476'use a component class as a mixin. Instead, just use a regular object.'477);478invariant(479!ReactElement.isValidElement(spec),480'ReactCompositeComponent: You\'re attempting to ' +481'use a component as a mixin. Instead, just use a regular object.'482);483484var proto = Constructor.prototype;485486// By handling mixins before any other properties, we ensure the same487// chaining order is applied to methods with DEFINE_MANY policy, whether488// mixins are listed before or after these methods in the spec.489if (spec.hasOwnProperty(MIXINS_KEY)) {490RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);491}492493for (var name in spec) {494if (!spec.hasOwnProperty(name)) {495continue;496}497498if (name === MIXINS_KEY) {499// We have already handled mixins in a special case above500continue;501}502503var property = spec[name];504validateMethodOverride(proto, name);505506if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {507RESERVED_SPEC_KEYS[name](Constructor, property);508} else {509// Setup methods on prototype:510// The following member methods should not be automatically bound:511// 1. Expected ReactCompositeComponent methods (in the "interface").512// 2. Overridden methods (that were mixed in).513var isCompositeComponentMethod =514ReactCompositeComponentInterface.hasOwnProperty(name);515var isAlreadyDefined = proto.hasOwnProperty(name);516var markedDontBind = property && property.__reactDontBind;517var isFunction = typeof property === 'function';518var shouldAutoBind =519isFunction &&520!isCompositeComponentMethod &&521!isAlreadyDefined &&522!markedDontBind;523524if (shouldAutoBind) {525if (!proto.__reactAutoBindMap) {526proto.__reactAutoBindMap = {};527}528proto.__reactAutoBindMap[name] = property;529proto[name] = property;530} else {531if (isAlreadyDefined) {532var specPolicy = ReactCompositeComponentInterface[name];533534// These cases should already be caught by validateMethodOverride535invariant(536isCompositeComponentMethod && (537specPolicy === SpecPolicy.DEFINE_MANY_MERGED ||538specPolicy === SpecPolicy.DEFINE_MANY539),540'ReactCompositeComponent: Unexpected spec policy %s for key %s ' +541'when mixing in component specs.',542specPolicy,543name544);545546// For methods which are defined more than once, call the existing547// methods before calling the new property, merging if appropriate.548if (specPolicy === SpecPolicy.DEFINE_MANY_MERGED) {549proto[name] = createMergedResultFunction(proto[name], property);550} else if (specPolicy === SpecPolicy.DEFINE_MANY) {551proto[name] = createChainedFunction(proto[name], property);552}553} else {554proto[name] = property;555if (__DEV__) {556// Add verbose displayName to the function, which helps when looking557// at profiling tools.558if (typeof property === 'function' && spec.displayName) {559proto[name].displayName = spec.displayName + '_' + name;560}561}562}563}564}565}566}567568function mixStaticSpecIntoComponent(Constructor, statics) {569if (!statics) {570return;571}572for (var name in statics) {573var property = statics[name];574if (!statics.hasOwnProperty(name)) {575continue;576}577578var isReserved = name in RESERVED_SPEC_KEYS;579invariant(580!isReserved,581'ReactCompositeComponent: You are attempting to define a reserved ' +582'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' +583'as an instance property instead; it will still be accessible on the ' +584'constructor.',585name586);587588var isInherited = name in Constructor;589invariant(590!isInherited,591'ReactCompositeComponent: You are attempting to define ' +592'`%s` on your component more than once. This conflict may be ' +593'due to a mixin.',594name595);596Constructor[name] = property;597}598}599600/**601* Merge two objects, but throw if both contain the same key.602*603* @param {object} one The first object, which is mutated.604* @param {object} two The second object605* @return {object} one after it has been mutated to contain everything in two.606*/607function mergeObjectsWithNoDuplicateKeys(one, two) {608invariant(609one && two && typeof one === 'object' && typeof two === 'object',610'mergeObjectsWithNoDuplicateKeys(): Cannot merge non-objects'611);612613mapObject(two, function(value, key) {614invariant(615one[key] === undefined,616'mergeObjectsWithNoDuplicateKeys(): ' +617'Tried to merge two objects with the same key: `%s`. This conflict ' +618'may be due to a mixin; in particular, this may be caused by two ' +619'getInitialState() or getDefaultProps() methods returning objects ' +620'with clashing keys.',621key622);623one[key] = value;624});625return one;626}627628/**629* Creates a function that invokes two functions and merges their return values.630*631* @param {function} one Function to invoke first.632* @param {function} two Function to invoke second.633* @return {function} Function that invokes the two argument functions.634* @private635*/636function createMergedResultFunction(one, two) {637return function mergedResult() {638var a = one.apply(this, arguments);639var b = two.apply(this, arguments);640if (a == null) {641return b;642} else if (b == null) {643return a;644}645return mergeObjectsWithNoDuplicateKeys(a, b);646};647}648649/**650* Creates a function that invokes two functions and ignores their return vales.651*652* @param {function} one Function to invoke first.653* @param {function} two Function to invoke second.654* @return {function} Function that invokes the two argument functions.655* @private656*/657function createChainedFunction(one, two) {658return function chainedFunction() {659one.apply(this, arguments);660two.apply(this, arguments);661};662}663664/**665* `ReactCompositeComponent` maintains an auxiliary life cycle state in666* `this._compositeLifeCycleState` (which can be null).667*668* This is different from the life cycle state maintained by `ReactComponent` in669* `this._lifeCycleState`. The following diagram shows how the states overlap in670* time. There are times when the CompositeLifeCycle is null - at those times it671* is only meaningful to look at ComponentLifeCycle alone.672*673* Top Row: ReactComponent.ComponentLifeCycle674* Low Row: ReactComponent.CompositeLifeCycle675*676* +-------+---------------------------------+--------+677* | UN | MOUNTED | UN |678* |MOUNTED| | MOUNTED|679* +-------+---------------------------------+--------+680* | ^--------+ +-------+ +--------^ |681* | | | | | | | |682* | 0--|MOUNTING|-0-|RECEIVE|-0-| UN |--->0 |683* | | | |PROPS | |MOUNTING| |684* | | | | | | | |685* | | | | | | | |686* | +--------+ +-------+ +--------+ |687* | | | |688* +-------+---------------------------------+--------+689*/690var CompositeLifeCycle = keyMirror({691/**692* Components in the process of being mounted respond to state changes693* differently.694*/695MOUNTING: null,696/**697* Components in the process of being unmounted are guarded against state698* changes.699*/700UNMOUNTING: null,701/**702* Components that are mounted and receiving new props respond to state703* changes differently.704*/705RECEIVING_PROPS: null706});707708/**709* @lends {ReactCompositeComponent.prototype}710*/711var ReactCompositeComponentMixin = {712713/**714* Base constructor for all composite component.715*716* @param {ReactElement} element717* @final718* @internal719*/720construct: function(element) {721// Children can be either an array or more than one argument722ReactComponent.Mixin.construct.apply(this, arguments);723ReactOwner.Mixin.construct.apply(this, arguments);724725this.state = null;726this._pendingState = null;727728// This is the public post-processed context. The real context and pending729// context lives on the element.730this.context = null;731732this._compositeLifeCycleState = null;733},734735/**736* Checks whether or not this composite component is mounted.737* @return {boolean} True if mounted, false otherwise.738* @protected739* @final740*/741isMounted: function() {742return ReactComponent.Mixin.isMounted.call(this) &&743this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING;744},745746/**747* Initializes the component, renders markup, and registers event listeners.748*749* @param {string} rootID DOM ID of the root node.750* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction751* @param {number} mountDepth number of components in the owner hierarchy752* @return {?string} Rendered markup to be inserted into the DOM.753* @final754* @internal755*/756mountComponent: ReactPerf.measure(757'ReactCompositeComponent',758'mountComponent',759function(rootID, transaction, mountDepth) {760ReactComponent.Mixin.mountComponent.call(761this,762rootID,763transaction,764mountDepth765);766this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;767768if (this.__reactAutoBindMap) {769this._bindAutoBindMethods();770}771772this.context = this._processContext(this._currentElement._context);773this.props = this._processProps(this.props);774775this.state = this.getInitialState ? this.getInitialState() : null;776invariant(777typeof this.state === 'object' && !Array.isArray(this.state),778'%s.getInitialState(): must return an object or null',779this.constructor.displayName || 'ReactCompositeComponent'780);781782this._pendingState = null;783this._pendingForceUpdate = false;784785if (this.componentWillMount) {786this.componentWillMount();787// When mounting, calls to `setState` by `componentWillMount` will set788// `this._pendingState` without triggering a re-render.789if (this._pendingState) {790this.state = this._pendingState;791this._pendingState = null;792}793}794795this._renderedComponent = instantiateReactComponent(796this._renderValidatedComponent(),797this._currentElement.type // The wrapping type798);799800// Done with mounting, `setState` will now trigger UI changes.801this._compositeLifeCycleState = null;802var markup = this._renderedComponent.mountComponent(803rootID,804transaction,805mountDepth + 1806);807if (this.componentDidMount) {808transaction.getReactMountReady().enqueue(this.componentDidMount, this);809}810return markup;811}812),813814/**815* Releases any resources allocated by `mountComponent`.816*817* @final818* @internal819*/820unmountComponent: function() {821this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;822if (this.componentWillUnmount) {823this.componentWillUnmount();824}825this._compositeLifeCycleState = null;826827this._renderedComponent.unmountComponent();828this._renderedComponent = null;829830ReactComponent.Mixin.unmountComponent.call(this);831832// Some existing components rely on this.props even after they've been833// destroyed (in event handlers).834// TODO: this.props = null;835// TODO: this.state = null;836},837838/**839* Sets a subset of the state. Always use this or `replaceState` to mutate840* state. You should treat `this.state` as immutable.841*842* There is no guarantee that `this.state` will be immediately updated, so843* accessing `this.state` after calling this method may return the old value.844*845* There is no guarantee that calls to `setState` will run synchronously,846* as they may eventually be batched together. You can provide an optional847* callback that will be executed when the call to setState is actually848* completed.849*850* @param {object} partialState Next partial state to be merged with state.851* @param {?function} callback Called after state is updated.852* @final853* @protected854*/855setState: function(partialState, callback) {856invariant(857typeof partialState === 'object' || partialState == null,858'setState(...): takes an object of state variables to update.'859);860if (__DEV__){861warning(862partialState != null,863'setState(...): You passed an undefined or null state object; ' +864'instead, use forceUpdate().'865);866}867// Merge with `_pendingState` if it exists, otherwise with existing state.868this.replaceState(869assign({}, this._pendingState || this.state, partialState),870callback871);872},873874/**875* Replaces all of the state. Always use this or `setState` to mutate state.876* You should treat `this.state` as immutable.877*878* There is no guarantee that `this.state` will be immediately updated, so879* accessing `this.state` after calling this method may return the old value.880*881* @param {object} completeState Next state.882* @param {?function} callback Called after state is updated.883* @final884* @protected885*/886replaceState: function(completeState, callback) {887validateLifeCycleOnReplaceState(this);888this._pendingState = completeState;889if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) {890// If we're in a componentWillMount handler, don't enqueue a rerender891// because ReactUpdates assumes we're in a browser context (which is wrong892// for server rendering) and we're about to do a render anyway.893// TODO: The callback here is ignored when setState is called from894// componentWillMount. Either fix it or disallow doing so completely in895// favor of getInitialState.896ReactUpdates.enqueueUpdate(this, callback);897}898},899900/**901* Filters the context object to only contain keys specified in902* `contextTypes`, and asserts that they are valid.903*904* @param {object} context905* @return {?object}906* @private907*/908_processContext: function(context) {909var maskedContext = null;910var contextTypes = this.constructor.contextTypes;911if (contextTypes) {912maskedContext = {};913for (var contextName in contextTypes) {914maskedContext[contextName] = context[contextName];915}916if (__DEV__) {917this._checkPropTypes(918contextTypes,919maskedContext,920ReactPropTypeLocations.context921);922}923}924return maskedContext;925},926927/**928* @param {object} currentContext929* @return {object}930* @private931*/932_processChildContext: function(currentContext) {933var childContext = this.getChildContext && this.getChildContext();934var displayName = this.constructor.displayName || 'ReactCompositeComponent';935if (childContext) {936invariant(937typeof this.constructor.childContextTypes === 'object',938'%s.getChildContext(): childContextTypes must be defined in order to ' +939'use getChildContext().',940displayName941);942if (__DEV__) {943this._checkPropTypes(944this.constructor.childContextTypes,945childContext,946ReactPropTypeLocations.childContext947);948}949for (var name in childContext) {950invariant(951name in this.constructor.childContextTypes,952'%s.getChildContext(): key "%s" is not defined in childContextTypes.',953displayName,954name955);956}957return assign({}, currentContext, childContext);958}959return currentContext;960},961962/**963* Processes props by setting default values for unspecified props and964* asserting that the props are valid. Does not mutate its argument; returns965* a new props object with defaults merged in.966*967* @param {object} newProps968* @return {object}969* @private970*/971_processProps: function(newProps) {972if (__DEV__) {973var propTypes = this.constructor.propTypes;974if (propTypes) {975this._checkPropTypes(propTypes, newProps, ReactPropTypeLocations.prop);976}977}978return newProps;979},980981/**982* Assert that the props are valid983*984* @param {object} propTypes Map of prop name to a ReactPropType985* @param {object} props986* @param {string} location e.g. "prop", "context", "child context"987* @private988*/989_checkPropTypes: function(propTypes, props, location) {990// TODO: Stop validating prop types here and only use the element991// validation.992var componentName = this.constructor.displayName;993for (var propName in propTypes) {994if (propTypes.hasOwnProperty(propName)) {995var error =996propTypes[propName](props, propName, componentName, location);997if (error instanceof Error) {998// We may want to extend this logic for similar errors in999// renderComponent calls, so I'm abstracting it away into1000// a function to minimize refactoring in the future1001var addendum = getDeclarationErrorAddendum(this);1002warning(false, error.message + addendum);1003}1004}1005}1006},10071008/**1009* If any of `_pendingElement`, `_pendingState`, or `_pendingForceUpdate`1010* is set, update the component.1011*1012* @param {ReactReconcileTransaction} transaction1013* @internal1014*/1015performUpdateIfNecessary: function(transaction) {1016var compositeLifeCycleState = this._compositeLifeCycleState;1017// Do not trigger a state transition if we are in the middle of mounting or1018// receiving props because both of those will already be doing this.1019if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING ||1020compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) {1021return;1022}10231024if (this._pendingElement == null &&1025this._pendingState == null &&1026!this._pendingForceUpdate) {1027return;1028}10291030var nextContext = this.context;1031var nextProps = this.props;1032var nextElement = this._currentElement;1033if (this._pendingElement != null) {1034nextElement = this._pendingElement;1035nextContext = this._processContext(nextElement._context);1036nextProps = this._processProps(nextElement.props);1037this._pendingElement = null;10381039this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;1040if (this.componentWillReceiveProps) {1041this.componentWillReceiveProps(nextProps, nextContext);1042}1043}10441045this._compositeLifeCycleState = null;10461047var nextState = this._pendingState || this.state;1048this._pendingState = null;10491050var shouldUpdate =1051this._pendingForceUpdate ||1052!this.shouldComponentUpdate ||1053this.shouldComponentUpdate(nextProps, nextState, nextContext);10541055if (__DEV__) {1056if (typeof shouldUpdate === "undefined") {1057console.warn(1058(this.constructor.displayName || 'ReactCompositeComponent') +1059'.shouldComponentUpdate(): Returned undefined instead of a ' +1060'boolean value. Make sure to return true or false.'1061);1062}1063}10641065if (shouldUpdate) {1066this._pendingForceUpdate = false;1067// Will set `this.props`, `this.state` and `this.context`.1068this._performComponentUpdate(1069nextElement,1070nextProps,1071nextState,1072nextContext,1073transaction1074);1075} else {1076// If it's determined that a component should not update, we still want1077// to set props and state.1078this._currentElement = nextElement;1079this.props = nextProps;1080this.state = nextState;1081this.context = nextContext;10821083// Owner cannot change because shouldUpdateReactComponent doesn't allow1084// it. TODO: Remove this._owner completely.1085this._owner = nextElement._owner;1086}1087},10881089/**1090* Merges new props and state, notifies delegate methods of update and1091* performs update.1092*1093* @param {ReactElement} nextElement Next element1094* @param {object} nextProps Next public object to set as properties.1095* @param {?object} nextState Next object to set as state.1096* @param {?object} nextContext Next public object to set as context.1097* @param {ReactReconcileTransaction} transaction1098* @private1099*/1100_performComponentUpdate: function(1101nextElement,1102nextProps,1103nextState,1104nextContext,1105transaction1106) {1107var prevElement = this._currentElement;1108var prevProps = this.props;1109var prevState = this.state;1110var prevContext = this.context;11111112if (this.componentWillUpdate) {1113this.componentWillUpdate(nextProps, nextState, nextContext);1114}11151116this._currentElement = nextElement;1117this.props = nextProps;1118this.state = nextState;1119this.context = nextContext;11201121// Owner cannot change because shouldUpdateReactComponent doesn't allow1122// it. TODO: Remove this._owner completely.1123this._owner = nextElement._owner;11241125this.updateComponent(1126transaction,1127prevElement1128);11291130if (this.componentDidUpdate) {1131transaction.getReactMountReady().enqueue(1132this.componentDidUpdate.bind(this, prevProps, prevState, prevContext),1133this1134);1135}1136},11371138receiveComponent: function(nextElement, transaction) {1139if (nextElement === this._currentElement &&1140nextElement._owner != null) {1141// Since elements are immutable after the owner is rendered,1142// we can do a cheap identity compare here to determine if this is a1143// superfluous reconcile. It's possible for state to be mutable but such1144// change should trigger an update of the owner which would recreate1145// the element. We explicitly check for the existence of an owner since1146// it's possible for a element created outside a composite to be1147// deeply mutated and reused.1148return;1149}11501151ReactComponent.Mixin.receiveComponent.call(1152this,1153nextElement,1154transaction1155);1156},11571158/**1159* Updates the component's currently mounted DOM representation.1160*1161* By default, this implements React's rendering and reconciliation algorithm.1162* Sophisticated clients may wish to override this.1163*1164* @param {ReactReconcileTransaction} transaction1165* @param {ReactElement} prevElement1166* @internal1167* @overridable1168*/1169updateComponent: ReactPerf.measure(1170'ReactCompositeComponent',1171'updateComponent',1172function(transaction, prevParentElement) {1173ReactComponent.Mixin.updateComponent.call(1174this,1175transaction,1176prevParentElement1177);11781179var prevComponentInstance = this._renderedComponent;1180var prevElement = prevComponentInstance._currentElement;1181var nextElement = this._renderValidatedComponent();1182if (shouldUpdateReactComponent(prevElement, nextElement)) {1183prevComponentInstance.receiveComponent(nextElement, transaction);1184} else {1185// These two IDs are actually the same! But nothing should rely on that.1186var thisID = this._rootNodeID;1187var prevComponentID = prevComponentInstance._rootNodeID;1188prevComponentInstance.unmountComponent();1189this._renderedComponent = instantiateReactComponent(1190nextElement,1191this._currentElement.type1192);1193var nextMarkup = this._renderedComponent.mountComponent(1194thisID,1195transaction,1196this._mountDepth + 11197);1198ReactComponent.BackendIDOperations.dangerouslyReplaceNodeWithMarkupByID(1199prevComponentID,1200nextMarkup1201);1202}1203}1204),12051206/**1207* Forces an update. This should only be invoked when it is known with1208* certainty that we are **not** in a DOM transaction.1209*1210* You may want to call this when you know that some deeper aspect of the1211* component's state has changed but `setState` was not called.1212*1213* This will not invoke `shouldUpdateComponent`, but it will invoke1214* `componentWillUpdate` and `componentDidUpdate`.1215*1216* @param {?function} callback Called after update is complete.1217* @final1218* @protected1219*/1220forceUpdate: function(callback) {1221var compositeLifeCycleState = this._compositeLifeCycleState;1222invariant(1223this.isMounted() ||1224compositeLifeCycleState === CompositeLifeCycle.MOUNTING,1225'forceUpdate(...): Can only force an update on mounted or mounting ' +1226'components.'1227);1228invariant(1229compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING &&1230ReactCurrentOwner.current == null,1231'forceUpdate(...): Cannot force an update while unmounting component ' +1232'or within a `render` function.'1233);1234this._pendingForceUpdate = true;1235ReactUpdates.enqueueUpdate(this, callback);1236},12371238/**1239* @private1240*/1241_renderValidatedComponent: ReactPerf.measure(1242'ReactCompositeComponent',1243'_renderValidatedComponent',1244function() {1245var renderedComponent;1246var previousContext = ReactContext.current;1247ReactContext.current = this._processChildContext(1248this._currentElement._context1249);1250ReactCurrentOwner.current = this;1251try {1252renderedComponent = this.render();1253if (renderedComponent === null || renderedComponent === false) {1254renderedComponent = ReactEmptyComponent.getEmptyComponent();1255ReactEmptyComponent.registerNullComponentID(this._rootNodeID);1256} else {1257ReactEmptyComponent.deregisterNullComponentID(this._rootNodeID);1258}1259} finally {1260ReactContext.current = previousContext;1261ReactCurrentOwner.current = null;1262}1263invariant(1264ReactElement.isValidElement(renderedComponent),1265'%s.render(): A valid ReactComponent must be returned. You may have ' +1266'returned undefined, an array or some other invalid object.',1267this.constructor.displayName || 'ReactCompositeComponent'1268);1269return renderedComponent;1270}1271),12721273/**1274* @private1275*/1276_bindAutoBindMethods: function() {1277for (var autoBindKey in this.__reactAutoBindMap) {1278if (!this.__reactAutoBindMap.hasOwnProperty(autoBindKey)) {1279continue;1280}1281var method = this.__reactAutoBindMap[autoBindKey];1282this[autoBindKey] = this._bindAutoBindMethod(ReactErrorUtils.guard(1283method,1284this.constructor.displayName + '.' + autoBindKey1285));1286}1287},12881289/**1290* Binds a method to the component.1291*1292* @param {function} method Method to be bound.1293* @private1294*/1295_bindAutoBindMethod: function(method) {1296var component = this;1297var boundMethod = method.bind(component);1298if (__DEV__) {1299boundMethod.__reactBoundContext = component;1300boundMethod.__reactBoundMethod = method;1301boundMethod.__reactBoundArguments = null;1302var componentName = component.constructor.displayName;1303var _bind = boundMethod.bind;1304boundMethod.bind = function(newThis, ...args) {1305// User is trying to bind() an autobound method; we effectively will1306// ignore the value of "this" that the user is trying to use, so1307// let's warn.1308if (newThis !== component && newThis !== null) {1309monitorCodeUse('react_bind_warning', { component: componentName });1310console.warn(1311'bind(): React component methods may only be bound to the ' +1312'component instance. See ' + componentName1313);1314} else if (!args.length) {1315monitorCodeUse('react_bind_warning', { component: componentName });1316console.warn(1317'bind(): You are binding a component method to the component. ' +1318'React does this for you automatically in a high-performance ' +1319'way, so you can safely remove this call. See ' + componentName1320);1321return boundMethod;1322}1323var reboundMethod = _bind.apply(boundMethod, arguments);1324reboundMethod.__reactBoundContext = component;1325reboundMethod.__reactBoundMethod = method;1326reboundMethod.__reactBoundArguments = args;1327return reboundMethod;1328};1329}1330return boundMethod;1331}1332};13331334var ReactCompositeComponentBase = function() {};1335assign(1336ReactCompositeComponentBase.prototype,1337ReactComponent.Mixin,1338ReactOwner.Mixin,1339ReactPropTransferer.Mixin,1340ReactCompositeComponentMixin1341);13421343/**1344* Module for creating composite components.1345*1346* @class ReactCompositeComponent1347* @extends ReactComponent1348* @extends ReactOwner1349* @extends ReactPropTransferer1350*/1351var ReactCompositeComponent = {13521353LifeCycle: CompositeLifeCycle,13541355Base: ReactCompositeComponentBase,13561357/**1358* Creates a composite component class given a class specification.1359*1360* @param {object} spec Class specification (which must define `render`).1361* @return {function} Component constructor function.1362* @public1363*/1364createClass: function(spec) {1365var Constructor = function(props) {1366// This constructor is overridden by mocks. The argument is used1367// by mocks to assert on what gets mounted. This will later be used1368// by the stand-alone class implementation.1369};1370Constructor.prototype = new ReactCompositeComponentBase();1371Constructor.prototype.constructor = Constructor;13721373injectedMixins.forEach(1374mixSpecIntoComponent.bind(null, Constructor)1375);13761377mixSpecIntoComponent(Constructor, spec);13781379// Initialize the defaultProps property after all mixins have been merged1380if (Constructor.getDefaultProps) {1381Constructor.defaultProps = Constructor.getDefaultProps();1382}13831384invariant(1385Constructor.prototype.render,1386'createClass(...): Class specification must implement a `render` method.'1387);13881389if (__DEV__) {1390if (Constructor.prototype.componentShouldUpdate) {1391monitorCodeUse(1392'react_component_should_update_warning',1393{ component: spec.displayName }1394);1395console.warn(1396(spec.displayName || 'A component') + ' has a method called ' +1397'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +1398'The name is phrased as a question because the function is ' +1399'expected to return a value.'1400);1401}1402}14031404// Reduce time spent doing lookups by setting these on the prototype.1405for (var methodName in ReactCompositeComponentInterface) {1406if (!Constructor.prototype[methodName]) {1407Constructor.prototype[methodName] = null;1408}1409}14101411if (__DEV__) {1412return ReactLegacyElement.wrapFactory(1413ReactElementValidator.createFactory(Constructor)1414);1415}1416return ReactLegacyElement.wrapFactory(1417ReactElement.createFactory(Constructor)1418);1419},14201421injection: {1422injectMixin: function(mixin) {1423injectedMixins.push(mixin);1424}1425}1426};14271428module.exports = ReactCompositeComponent;142914301431