UNPKG

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