UNPKG

35.1 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var _prodInvariant = require('./reactProdInvariant'),
12 _assign = require('object-assign');
13
14var React = require('react/lib/React');
15var ReactComponentEnvironment = require('./ReactComponentEnvironment');
16var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
17var ReactErrorUtils = require('./ReactErrorUtils');
18var ReactInstanceMap = require('./ReactInstanceMap');
19var ReactInstrumentation = require('./ReactInstrumentation');
20var ReactNodeTypes = require('./ReactNodeTypes');
21var ReactReconciler = require('./ReactReconciler');
22
23if (process.env.NODE_ENV !== 'production') {
24 var checkReactTypeSpec = require('./checkReactTypeSpec');
25}
26
27var emptyObject = require('fbjs/lib/emptyObject');
28var invariant = require('fbjs/lib/invariant');
29var shallowEqual = require('fbjs/lib/shallowEqual');
30var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
31var warning = require('fbjs/lib/warning');
32
33var CompositeTypes = {
34 ImpureClass: 0,
35 PureClass: 1,
36 StatelessFunctional: 2
37};
38
39function StatelessComponent(Component) {}
40StatelessComponent.prototype.render = function () {
41 var Component = ReactInstanceMap.get(this)._currentElement.type;
42 var element = Component(this.props, this.context, this.updater);
43 warnIfInvalidElement(Component, element);
44 return element;
45};
46
47function warnIfInvalidElement(Component, element) {
48 if (process.env.NODE_ENV !== 'production') {
49 process.env.NODE_ENV !== 'production' ? warning(element === null || element === false || React.isValidElement(element), '%s(...): A valid React element (or null) must be returned. You may have ' + 'returned undefined, an array or some other invalid object.', Component.displayName || Component.name || 'Component') : void 0;
50 process.env.NODE_ENV !== 'production' ? warning(!Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a functional component.', Component.displayName || Component.name || 'Component') : void 0;
51 }
52}
53
54function shouldConstruct(Component) {
55 return !!(Component.prototype && Component.prototype.isReactComponent);
56}
57
58function isPureComponent(Component) {
59 return !!(Component.prototype && Component.prototype.isPureReactComponent);
60}
61
62// Separated into a function to contain deoptimizations caused by try/finally.
63function measureLifeCyclePerf(fn, debugID, timerType) {
64 if (debugID === 0) {
65 // Top-level wrappers (see ReactMount) and empty components (see
66 // ReactDOMEmptyComponent) are invisible to hooks and devtools.
67 // Both are implementation details that should go away in the future.
68 return fn();
69 }
70
71 ReactInstrumentation.debugTool.onBeginLifeCycleTimer(debugID, timerType);
72 try {
73 return fn();
74 } finally {
75 ReactInstrumentation.debugTool.onEndLifeCycleTimer(debugID, timerType);
76 }
77}
78
79/**
80 * ------------------ The Life-Cycle of a Composite Component ------------------
81 *
82 * - constructor: Initialization of state. The instance is now retained.
83 * - componentWillMount
84 * - render
85 * - [children's constructors]
86 * - [children's componentWillMount and render]
87 * - [children's componentDidMount]
88 * - componentDidMount
89 *
90 * Update Phases:
91 * - componentWillReceiveProps (only called if parent updated)
92 * - shouldComponentUpdate
93 * - componentWillUpdate
94 * - render
95 * - [children's constructors or receive props phases]
96 * - componentDidUpdate
97 *
98 * - componentWillUnmount
99 * - [children's componentWillUnmount]
100 * - [children destroyed]
101 * - (destroyed): The instance is now blank, released by React and ready for GC.
102 *
103 * -----------------------------------------------------------------------------
104 */
105
106/**
107 * An incrementing ID assigned to each component when it is mounted. This is
108 * used to enforce the order in which `ReactUpdates` updates dirty components.
109 *
110 * @private
111 */
112var nextMountID = 1;
113
114/**
115 * @lends {ReactCompositeComponent.prototype}
116 */
117var ReactCompositeComponent = {
118 /**
119 * Base constructor for all composite component.
120 *
121 * @param {ReactElement} element
122 * @final
123 * @internal
124 */
125 construct: function (element) {
126 this._currentElement = element;
127 this._rootNodeID = 0;
128 this._compositeType = null;
129 this._instance = null;
130 this._hostParent = null;
131 this._hostContainerInfo = null;
132
133 // See ReactUpdateQueue
134 this._updateBatchNumber = null;
135 this._pendingElement = null;
136 this._pendingStateQueue = null;
137 this._pendingReplaceState = false;
138 this._pendingForceUpdate = false;
139
140 this._renderedNodeType = null;
141 this._renderedComponent = null;
142 this._context = null;
143 this._mountOrder = 0;
144 this._topLevelWrapper = null;
145
146 // See ReactUpdates and ReactUpdateQueue.
147 this._pendingCallbacks = null;
148
149 // ComponentWillUnmount shall only be called once
150 this._calledComponentWillUnmount = false;
151
152 if (process.env.NODE_ENV !== 'production') {
153 this._warnedAboutRefsInRender = false;
154 }
155 },
156
157 /**
158 * Initializes the component, renders markup, and registers event listeners.
159 *
160 * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
161 * @param {?object} hostParent
162 * @param {?object} hostContainerInfo
163 * @param {?object} context
164 * @return {?string} Rendered markup to be inserted into the DOM.
165 * @final
166 * @internal
167 */
168 mountComponent: function (transaction, hostParent, hostContainerInfo, context) {
169 var _this = this;
170
171 this._context = context;
172 this._mountOrder = nextMountID++;
173 this._hostParent = hostParent;
174 this._hostContainerInfo = hostContainerInfo;
175
176 var publicProps = this._currentElement.props;
177 var publicContext = this._processContext(context);
178
179 var Component = this._currentElement.type;
180
181 var updateQueue = transaction.getUpdateQueue();
182
183 // Initialize the public class
184 var doConstruct = shouldConstruct(Component);
185 var inst = this._constructComponent(doConstruct, publicProps, publicContext, updateQueue);
186 var renderedElement;
187
188 // Support functional components
189 if (!doConstruct && (inst == null || inst.render == null)) {
190 renderedElement = inst;
191 warnIfInvalidElement(Component, renderedElement);
192 !(inst === null || inst === false || React.isValidElement(inst)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.', Component.displayName || Component.name || 'Component') : _prodInvariant('105', Component.displayName || Component.name || 'Component') : void 0;
193 inst = new StatelessComponent(Component);
194 this._compositeType = CompositeTypes.StatelessFunctional;
195 } else {
196 if (isPureComponent(Component)) {
197 this._compositeType = CompositeTypes.PureClass;
198 } else {
199 this._compositeType = CompositeTypes.ImpureClass;
200 }
201 }
202
203 if (process.env.NODE_ENV !== 'production') {
204 // This will throw later in _renderValidatedComponent, but add an early
205 // warning now to help debugging
206 if (inst.render == null) {
207 process.env.NODE_ENV !== 'production' ? warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', Component.displayName || Component.name || 'Component') : void 0;
208 }
209
210 var propsMutated = inst.props !== publicProps;
211 var componentName = Component.displayName || Component.name || 'Component';
212
213 process.env.NODE_ENV !== 'production' ? warning(inst.props === undefined || !propsMutated, '%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", componentName, componentName) : void 0;
214 }
215
216 // These should be set up in the constructor, but as a convenience for
217 // simpler class abstractions, we set them up after the fact.
218 inst.props = publicProps;
219 inst.context = publicContext;
220 inst.refs = emptyObject;
221 inst.updater = updateQueue;
222
223 this._instance = inst;
224
225 // Store a reference from the instance back to the internal representation
226 ReactInstanceMap.set(inst, this);
227
228 if (process.env.NODE_ENV !== 'production') {
229 // Since plain JS classes are defined without any special initialization
230 // logic, we can not catch common errors early. Therefore, we have to
231 // catch them here, at initialization time, instead.
232 process.env.NODE_ENV !== 'production' ? warning(!inst.getInitialState || inst.getInitialState.isReactClassApproved || inst.state, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', this.getName() || 'a component') : void 0;
233 process.env.NODE_ENV !== 'production' ? warning(!inst.getDefaultProps || inst.getDefaultProps.isReactClassApproved, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', this.getName() || 'a component') : void 0;
234 process.env.NODE_ENV !== 'production' ? warning(!inst.propTypes, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', this.getName() || 'a component') : void 0;
235 process.env.NODE_ENV !== 'production' ? warning(!inst.contextTypes, 'contextTypes was defined as an instance property on %s. Use a ' + 'static property to define contextTypes instead.', this.getName() || 'a component') : void 0;
236 process.env.NODE_ENV !== 'production' ? warning(typeof inst.componentShouldUpdate !== 'function', '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', this.getName() || 'A component') : void 0;
237 process.env.NODE_ENV !== 'production' ? warning(typeof inst.componentDidUnmount !== 'function', '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', this.getName() || 'A component') : void 0;
238 process.env.NODE_ENV !== 'production' ? warning(typeof inst.componentWillRecieveProps !== 'function', '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', this.getName() || 'A component') : void 0;
239 }
240
241 var initialState = inst.state;
242 if (initialState === undefined) {
243 inst.state = initialState = null;
244 }
245 !(typeof initialState === 'object' && !Array.isArray(initialState)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.state: must be set to an object or null', this.getName() || 'ReactCompositeComponent') : _prodInvariant('106', this.getName() || 'ReactCompositeComponent') : void 0;
246
247 this._pendingStateQueue = null;
248 this._pendingReplaceState = false;
249 this._pendingForceUpdate = false;
250
251 var markup;
252 if (inst.unstable_handleError) {
253 markup = this.performInitialMountWithErrorHandling(renderedElement, hostParent, hostContainerInfo, transaction, context);
254 } else {
255 markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
256 }
257
258 if (inst.componentDidMount) {
259 if (process.env.NODE_ENV !== 'production') {
260 transaction.getReactMountReady().enqueue(function () {
261 measureLifeCyclePerf(function () {
262 return inst.componentDidMount();
263 }, _this._debugID, 'componentDidMount');
264 });
265 } else {
266 transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
267 }
268 }
269
270 return markup;
271 },
272
273 _constructComponent: function (doConstruct, publicProps, publicContext, updateQueue) {
274 if (process.env.NODE_ENV !== 'production' && !doConstruct) {
275 ReactCurrentOwner.current = this;
276 try {
277 return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);
278 } finally {
279 ReactCurrentOwner.current = null;
280 }
281 } else {
282 return this._constructComponentWithoutOwner(doConstruct, publicProps, publicContext, updateQueue);
283 }
284 },
285
286 _constructComponentWithoutOwner: function (doConstruct, publicProps, publicContext, updateQueue) {
287 var Component = this._currentElement.type;
288
289 if (doConstruct) {
290 if (process.env.NODE_ENV !== 'production') {
291 return measureLifeCyclePerf(function () {
292 return new Component(publicProps, publicContext, updateQueue);
293 }, this._debugID, 'ctor');
294 } else {
295 return new Component(publicProps, publicContext, updateQueue);
296 }
297 }
298
299 // This can still be an instance in case of factory components
300 // but we'll count this as time spent rendering as the more common case.
301 if (process.env.NODE_ENV !== 'production') {
302 return measureLifeCyclePerf(function () {
303 return Component(publicProps, publicContext, updateQueue);
304 }, this._debugID, 'render');
305 } else {
306 return Component(publicProps, publicContext, updateQueue);
307 }
308 },
309
310 performInitialMountWithErrorHandling: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
311 var markup;
312 var checkpoint = transaction.checkpoint();
313 try {
314 markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
315 } catch (e) {
316 // Roll back to checkpoint, handle error (which may add items to the transaction), and take a new checkpoint
317 transaction.rollback(checkpoint);
318 this._instance.unstable_handleError(e);
319 if (this._pendingStateQueue) {
320 this._instance.state = this._processPendingState(this._instance.props, this._instance.context);
321 }
322 checkpoint = transaction.checkpoint();
323
324 this._renderedComponent.unmountComponent(true);
325 transaction.rollback(checkpoint);
326
327 // Try again - we've informed the component about the error, so they can render an error message this time.
328 // If this throws again, the error will bubble up (and can be caught by a higher error boundary).
329 markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
330 }
331 return markup;
332 },
333
334 performInitialMount: function (renderedElement, hostParent, hostContainerInfo, transaction, context) {
335 var inst = this._instance;
336
337 var debugID = 0;
338 if (process.env.NODE_ENV !== 'production') {
339 debugID = this._debugID;
340 }
341
342 if (inst.componentWillMount) {
343 if (process.env.NODE_ENV !== 'production') {
344 measureLifeCyclePerf(function () {
345 return inst.componentWillMount();
346 }, debugID, 'componentWillMount');
347 } else {
348 inst.componentWillMount();
349 }
350 // When mounting, calls to `setState` by `componentWillMount` will set
351 // `this._pendingStateQueue` without triggering a re-render.
352 if (this._pendingStateQueue) {
353 inst.state = this._processPendingState(inst.props, inst.context);
354 }
355 }
356
357 // If not a stateless component, we now render
358 if (renderedElement === undefined) {
359 renderedElement = this._renderValidatedComponent();
360 }
361
362 var nodeType = ReactNodeTypes.getType(renderedElement);
363 this._renderedNodeType = nodeType;
364 var child = this._instantiateReactComponent(renderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
365 );
366 this._renderedComponent = child;
367
368 var markup = ReactReconciler.mountComponent(child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), debugID);
369
370 if (process.env.NODE_ENV !== 'production') {
371 if (debugID !== 0) {
372 var childDebugIDs = child._debugID !== 0 ? [child._debugID] : [];
373 ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs);
374 }
375 }
376
377 return markup;
378 },
379
380 getHostNode: function () {
381 return ReactReconciler.getHostNode(this._renderedComponent);
382 },
383
384 /**
385 * Releases any resources allocated by `mountComponent`.
386 *
387 * @final
388 * @internal
389 */
390 unmountComponent: function (safely) {
391 if (!this._renderedComponent) {
392 return;
393 }
394
395 var inst = this._instance;
396
397 if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) {
398 inst._calledComponentWillUnmount = true;
399
400 if (safely) {
401 var name = this.getName() + '.componentWillUnmount()';
402 ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
403 } else {
404 if (process.env.NODE_ENV !== 'production') {
405 measureLifeCyclePerf(function () {
406 return inst.componentWillUnmount();
407 }, this._debugID, 'componentWillUnmount');
408 } else {
409 inst.componentWillUnmount();
410 }
411 }
412 }
413
414 if (this._renderedComponent) {
415 ReactReconciler.unmountComponent(this._renderedComponent, safely);
416 this._renderedNodeType = null;
417 this._renderedComponent = null;
418 this._instance = null;
419 }
420
421 // Reset pending fields
422 // Even if this component is scheduled for another update in ReactUpdates,
423 // it would still be ignored because these fields are reset.
424 this._pendingStateQueue = null;
425 this._pendingReplaceState = false;
426 this._pendingForceUpdate = false;
427 this._pendingCallbacks = null;
428 this._pendingElement = null;
429
430 // These fields do not really need to be reset since this object is no
431 // longer accessible.
432 this._context = null;
433 this._rootNodeID = 0;
434 this._topLevelWrapper = null;
435
436 // Delete the reference from the instance to this internal representation
437 // which allow the internals to be properly cleaned up even if the user
438 // leaks a reference to the public instance.
439 ReactInstanceMap.remove(inst);
440
441 // Some existing components rely on inst.props even after they've been
442 // destroyed (in event handlers).
443 // TODO: inst.props = null;
444 // TODO: inst.state = null;
445 // TODO: inst.context = null;
446 },
447
448 /**
449 * Filters the context object to only contain keys specified in
450 * `contextTypes`
451 *
452 * @param {object} context
453 * @return {?object}
454 * @private
455 */
456 _maskContext: function (context) {
457 var Component = this._currentElement.type;
458 var contextTypes = Component.contextTypes;
459 if (!contextTypes) {
460 return emptyObject;
461 }
462 var maskedContext = {};
463 for (var contextName in contextTypes) {
464 maskedContext[contextName] = context[contextName];
465 }
466 return maskedContext;
467 },
468
469 /**
470 * Filters the context object to only contain keys specified in
471 * `contextTypes`, and asserts that they are valid.
472 *
473 * @param {object} context
474 * @return {?object}
475 * @private
476 */
477 _processContext: function (context) {
478 var maskedContext = this._maskContext(context);
479 if (process.env.NODE_ENV !== 'production') {
480 var Component = this._currentElement.type;
481 if (Component.contextTypes) {
482 this._checkContextTypes(Component.contextTypes, maskedContext, 'context');
483 }
484 }
485 return maskedContext;
486 },
487
488 /**
489 * @param {object} currentContext
490 * @return {object}
491 * @private
492 */
493 _processChildContext: function (currentContext) {
494 var Component = this._currentElement.type;
495 var inst = this._instance;
496 var childContext;
497
498 if (inst.getChildContext) {
499 if (process.env.NODE_ENV !== 'production') {
500 ReactInstrumentation.debugTool.onBeginProcessingChildContext();
501 try {
502 childContext = inst.getChildContext();
503 } finally {
504 ReactInstrumentation.debugTool.onEndProcessingChildContext();
505 }
506 } else {
507 childContext = inst.getChildContext();
508 }
509 }
510
511 if (childContext) {
512 !(typeof Component.childContextTypes === 'object') ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().', this.getName() || 'ReactCompositeComponent') : _prodInvariant('107', this.getName() || 'ReactCompositeComponent') : void 0;
513 if (process.env.NODE_ENV !== 'production') {
514 this._checkContextTypes(Component.childContextTypes, childContext, 'child context');
515 }
516 for (var name in childContext) {
517 !(name in Component.childContextTypes) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', this.getName() || 'ReactCompositeComponent', name) : _prodInvariant('108', this.getName() || 'ReactCompositeComponent', name) : void 0;
518 }
519 return _assign({}, currentContext, childContext);
520 }
521 return currentContext;
522 },
523
524 /**
525 * Assert that the context types are valid
526 *
527 * @param {object} typeSpecs Map of context field to a ReactPropType
528 * @param {object} values Runtime values that need to be type-checked
529 * @param {string} location e.g. "prop", "context", "child context"
530 * @private
531 */
532 _checkContextTypes: function (typeSpecs, values, location) {
533 if (process.env.NODE_ENV !== 'production') {
534 checkReactTypeSpec(typeSpecs, values, location, this.getName(), null, this._debugID);
535 }
536 },
537
538 receiveComponent: function (nextElement, transaction, nextContext) {
539 var prevElement = this._currentElement;
540 var prevContext = this._context;
541
542 this._pendingElement = null;
543
544 this.updateComponent(transaction, prevElement, nextElement, prevContext, nextContext);
545 },
546
547 /**
548 * If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate`
549 * is set, update the component.
550 *
551 * @param {ReactReconcileTransaction} transaction
552 * @internal
553 */
554 performUpdateIfNecessary: function (transaction) {
555 if (this._pendingElement != null) {
556 ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
557 } else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
558 this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
559 } else {
560 this._updateBatchNumber = null;
561 }
562 },
563
564 /**
565 * Perform an update to a mounted component. The componentWillReceiveProps and
566 * shouldComponentUpdate methods are called, then (assuming the update isn't
567 * skipped) the remaining update lifecycle methods are called and the DOM
568 * representation is updated.
569 *
570 * By default, this implements React's rendering and reconciliation algorithm.
571 * Sophisticated clients may wish to override this.
572 *
573 * @param {ReactReconcileTransaction} transaction
574 * @param {ReactElement} prevParentElement
575 * @param {ReactElement} nextParentElement
576 * @internal
577 * @overridable
578 */
579 updateComponent: function (transaction, prevParentElement, nextParentElement, prevUnmaskedContext, nextUnmaskedContext) {
580 var inst = this._instance;
581 !(inst != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Attempted to update component `%s` that has already been unmounted (or failed to mount).', this.getName() || 'ReactCompositeComponent') : _prodInvariant('136', this.getName() || 'ReactCompositeComponent') : void 0;
582
583 var willReceive = false;
584 var nextContext;
585
586 // Determine if the context has changed or not
587 if (this._context === nextUnmaskedContext) {
588 nextContext = inst.context;
589 } else {
590 nextContext = this._processContext(nextUnmaskedContext);
591 willReceive = true;
592 }
593
594 var prevProps = prevParentElement.props;
595 var nextProps = nextParentElement.props;
596
597 // Not a simple state update but a props update
598 if (prevParentElement !== nextParentElement) {
599 willReceive = true;
600 }
601
602 // An update here will schedule an update but immediately set
603 // _pendingStateQueue which will ensure that any state updates gets
604 // immediately reconciled instead of waiting for the next batch.
605 if (willReceive && inst.componentWillReceiveProps) {
606 if (process.env.NODE_ENV !== 'production') {
607 measureLifeCyclePerf(function () {
608 return inst.componentWillReceiveProps(nextProps, nextContext);
609 }, this._debugID, 'componentWillReceiveProps');
610 } else {
611 inst.componentWillReceiveProps(nextProps, nextContext);
612 }
613 }
614
615 var nextState = this._processPendingState(nextProps, nextContext);
616 var shouldUpdate = true;
617
618 if (!this._pendingForceUpdate) {
619 if (inst.shouldComponentUpdate) {
620 if (process.env.NODE_ENV !== 'production') {
621 shouldUpdate = measureLifeCyclePerf(function () {
622 return inst.shouldComponentUpdate(nextProps, nextState, nextContext);
623 }, this._debugID, 'shouldComponentUpdate');
624 } else {
625 shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
626 }
627 } else {
628 if (this._compositeType === CompositeTypes.PureClass) {
629 shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
630 }
631 }
632 }
633
634 if (process.env.NODE_ENV !== 'production') {
635 process.env.NODE_ENV !== 'production' ? warning(shouldUpdate !== undefined, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', this.getName() || 'ReactCompositeComponent') : void 0;
636 }
637
638 this._updateBatchNumber = null;
639 if (shouldUpdate) {
640 this._pendingForceUpdate = false;
641 // Will set `this.props`, `this.state` and `this.context`.
642 this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);
643 } else {
644 // If it's determined that a component should not update, we still want
645 // to set props and state but we shortcut the rest of the update.
646 this._currentElement = nextParentElement;
647 this._context = nextUnmaskedContext;
648 inst.props = nextProps;
649 inst.state = nextState;
650 inst.context = nextContext;
651 }
652 },
653
654 _processPendingState: function (props, context) {
655 var inst = this._instance;
656 var queue = this._pendingStateQueue;
657 var replace = this._pendingReplaceState;
658 this._pendingReplaceState = false;
659 this._pendingStateQueue = null;
660
661 if (!queue) {
662 return inst.state;
663 }
664
665 if (replace && queue.length === 1) {
666 return queue[0];
667 }
668
669 var nextState = _assign({}, replace ? queue[0] : inst.state);
670 for (var i = replace ? 1 : 0; i < queue.length; i++) {
671 var partial = queue[i];
672 _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
673 }
674
675 return nextState;
676 },
677
678 /**
679 * Merges new props and state, notifies delegate methods of update and
680 * performs update.
681 *
682 * @param {ReactElement} nextElement Next element
683 * @param {object} nextProps Next public object to set as properties.
684 * @param {?object} nextState Next object to set as state.
685 * @param {?object} nextContext Next public object to set as context.
686 * @param {ReactReconcileTransaction} transaction
687 * @param {?object} unmaskedContext
688 * @private
689 */
690 _performComponentUpdate: function (nextElement, nextProps, nextState, nextContext, transaction, unmaskedContext) {
691 var _this2 = this;
692
693 var inst = this._instance;
694
695 var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
696 var prevProps;
697 var prevState;
698 var prevContext;
699 if (hasComponentDidUpdate) {
700 prevProps = inst.props;
701 prevState = inst.state;
702 prevContext = inst.context;
703 }
704
705 if (inst.componentWillUpdate) {
706 if (process.env.NODE_ENV !== 'production') {
707 measureLifeCyclePerf(function () {
708 return inst.componentWillUpdate(nextProps, nextState, nextContext);
709 }, this._debugID, 'componentWillUpdate');
710 } else {
711 inst.componentWillUpdate(nextProps, nextState, nextContext);
712 }
713 }
714
715 this._currentElement = nextElement;
716 this._context = unmaskedContext;
717 inst.props = nextProps;
718 inst.state = nextState;
719 inst.context = nextContext;
720
721 this._updateRenderedComponent(transaction, unmaskedContext);
722
723 if (hasComponentDidUpdate) {
724 if (process.env.NODE_ENV !== 'production') {
725 transaction.getReactMountReady().enqueue(function () {
726 measureLifeCyclePerf(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), _this2._debugID, 'componentDidUpdate');
727 });
728 } else {
729 transaction.getReactMountReady().enqueue(inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), inst);
730 }
731 }
732 },
733
734 /**
735 * Call the component's `render` method and update the DOM accordingly.
736 *
737 * @param {ReactReconcileTransaction} transaction
738 * @internal
739 */
740 _updateRenderedComponent: function (transaction, context) {
741 var prevComponentInstance = this._renderedComponent;
742 var prevRenderedElement = prevComponentInstance._currentElement;
743 var nextRenderedElement = this._renderValidatedComponent();
744
745 var debugID = 0;
746 if (process.env.NODE_ENV !== 'production') {
747 debugID = this._debugID;
748 }
749
750 if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
751 ReactReconciler.receiveComponent(prevComponentInstance, nextRenderedElement, transaction, this._processChildContext(context));
752 } else {
753 var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
754 ReactReconciler.unmountComponent(prevComponentInstance, false);
755
756 var nodeType = ReactNodeTypes.getType(nextRenderedElement);
757 this._renderedNodeType = nodeType;
758 var child = this._instantiateReactComponent(nextRenderedElement, nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
759 );
760 this._renderedComponent = child;
761
762 var nextMarkup = ReactReconciler.mountComponent(child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context), debugID);
763
764 if (process.env.NODE_ENV !== 'production') {
765 if (debugID !== 0) {
766 var childDebugIDs = child._debugID !== 0 ? [child._debugID] : [];
767 ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs);
768 }
769 }
770
771 this._replaceNodeWithMarkup(oldHostNode, nextMarkup, prevComponentInstance);
772 }
773 },
774
775 /**
776 * Overridden in shallow rendering.
777 *
778 * @protected
779 */
780 _replaceNodeWithMarkup: function (oldHostNode, nextMarkup, prevInstance) {
781 ReactComponentEnvironment.replaceNodeWithMarkup(oldHostNode, nextMarkup, prevInstance);
782 },
783
784 /**
785 * @protected
786 */
787 _renderValidatedComponentWithoutOwnerOrContext: function () {
788 var inst = this._instance;
789 var renderedElement;
790
791 if (process.env.NODE_ENV !== 'production') {
792 renderedElement = measureLifeCyclePerf(function () {
793 return inst.render();
794 }, this._debugID, 'render');
795 } else {
796 renderedElement = inst.render();
797 }
798
799 if (process.env.NODE_ENV !== 'production') {
800 // We allow auto-mocks to proceed as if they're returning null.
801 if (renderedElement === undefined && inst.render._isMockFunction) {
802 // This is probably bad practice. Consider warning here and
803 // deprecating this convenience.
804 renderedElement = null;
805 }
806 }
807
808 return renderedElement;
809 },
810
811 /**
812 * @private
813 */
814 _renderValidatedComponent: function () {
815 var renderedElement;
816 if (process.env.NODE_ENV !== 'production' || this._compositeType !== CompositeTypes.StatelessFunctional) {
817 ReactCurrentOwner.current = this;
818 try {
819 renderedElement = this._renderValidatedComponentWithoutOwnerOrContext();
820 } finally {
821 ReactCurrentOwner.current = null;
822 }
823 } else {
824 renderedElement = this._renderValidatedComponentWithoutOwnerOrContext();
825 }
826 !(
827 // TODO: An `isValidNode` function would probably be more appropriate
828 renderedElement === null || renderedElement === false || React.isValidElement(renderedElement)) ? process.env.NODE_ENV !== 'production' ? invariant(false, '%s.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.', this.getName() || 'ReactCompositeComponent') : _prodInvariant('109', this.getName() || 'ReactCompositeComponent') : void 0;
829
830 return renderedElement;
831 },
832
833 /**
834 * Lazily allocates the refs object and stores `component` as `ref`.
835 *
836 * @param {string} ref Reference name.
837 * @param {component} component Component to store as `ref`.
838 * @final
839 * @private
840 */
841 attachRef: function (ref, component) {
842 var inst = this.getPublicInstance();
843 !(inst != null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'Stateless function components cannot have refs.') : _prodInvariant('110') : void 0;
844 var publicComponentInstance = component.getPublicInstance();
845 if (process.env.NODE_ENV !== 'production') {
846 var componentName = component && component.getName ? component.getName() : 'a component';
847 process.env.NODE_ENV !== 'production' ? warning(publicComponentInstance != null || component._compositeType !== CompositeTypes.StatelessFunctional, 'Stateless function components cannot be given refs ' + '(See ref "%s" in %s created by %s). ' + 'Attempts to access this ref will fail.', ref, componentName, this.getName()) : void 0;
848 }
849 var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs;
850 refs[ref] = publicComponentInstance;
851 },
852
853 /**
854 * Detaches a reference name.
855 *
856 * @param {string} ref Name to dereference.
857 * @final
858 * @private
859 */
860 detachRef: function (ref) {
861 var refs = this.getPublicInstance().refs;
862 delete refs[ref];
863 },
864
865 /**
866 * Get a text description of the component that can be used to identify it
867 * in error messages.
868 * @return {string} The name or null.
869 * @internal
870 */
871 getName: function () {
872 var type = this._currentElement.type;
873 var constructor = this._instance && this._instance.constructor;
874 return type.displayName || constructor && constructor.displayName || type.name || constructor && constructor.name || null;
875 },
876
877 /**
878 * Get the publicly accessible representation of this component - i.e. what
879 * is exposed by refs and returned by render. Can be null for stateless
880 * components.
881 *
882 * @return {ReactComponent} the public component instance.
883 * @internal
884 */
885 getPublicInstance: function () {
886 var inst = this._instance;
887 if (this._compositeType === CompositeTypes.StatelessFunctional) {
888 return null;
889 }
890 return inst;
891 },
892
893 // Stub
894 _instantiateReactComponent: null
895};
896
897module.exports = ReactCompositeComponent;
\No newline at end of file