Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80517 views
1
/**
2
* Copyright 2013-2015, 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
* @providesModule ReactCompositeComponent
10
*/
11
12
'use strict';
13
14
var ReactComponentEnvironment = require("./ReactComponentEnvironment");
15
var ReactContext = require("./ReactContext");
16
var ReactCurrentOwner = require("./ReactCurrentOwner");
17
var ReactElement = require("./ReactElement");
18
var ReactElementValidator = require("./ReactElementValidator");
19
var ReactInstanceMap = require("./ReactInstanceMap");
20
var ReactLifeCycle = require("./ReactLifeCycle");
21
var ReactNativeComponent = require("./ReactNativeComponent");
22
var ReactPerf = require("./ReactPerf");
23
var ReactPropTypeLocations = require("./ReactPropTypeLocations");
24
var ReactPropTypeLocationNames = require("./ReactPropTypeLocationNames");
25
var ReactReconciler = require("./ReactReconciler");
26
var ReactUpdates = require("./ReactUpdates");
27
28
var assign = require("./Object.assign");
29
var emptyObject = require("./emptyObject");
30
var invariant = require("./invariant");
31
var shouldUpdateReactComponent = require("./shouldUpdateReactComponent");
32
var warning = require("./warning");
33
34
function getDeclarationErrorAddendum(component) {
35
var owner = component._currentElement._owner || null;
36
if (owner) {
37
var name = owner.getName();
38
if (name) {
39
return ' Check the render method of `' + name + '`.';
40
}
41
}
42
return '';
43
}
44
45
/**
46
* ------------------ The Life-Cycle of a Composite Component ------------------
47
*
48
* - constructor: Initialization of state. The instance is now retained.
49
* - componentWillMount
50
* - render
51
* - [children's constructors]
52
* - [children's componentWillMount and render]
53
* - [children's componentDidMount]
54
* - componentDidMount
55
*
56
* Update Phases:
57
* - componentWillReceiveProps (only called if parent updated)
58
* - shouldComponentUpdate
59
* - componentWillUpdate
60
* - render
61
* - [children's constructors or receive props phases]
62
* - componentDidUpdate
63
*
64
* - componentWillUnmount
65
* - [children's componentWillUnmount]
66
* - [children destroyed]
67
* - (destroyed): The instance is now blank, released by React and ready for GC.
68
*
69
* -----------------------------------------------------------------------------
70
*/
71
72
/**
73
* An incrementing ID assigned to each component when it is mounted. This is
74
* used to enforce the order in which `ReactUpdates` updates dirty components.
75
*
76
* @private
77
*/
78
var nextMountID = 1;
79
80
/**
81
* @lends {ReactCompositeComponent.prototype}
82
*/
83
var ReactCompositeComponentMixin = {
84
85
/**
86
* Base constructor for all composite component.
87
*
88
* @param {ReactElement} element
89
* @final
90
* @internal
91
*/
92
construct: function(element) {
93
this._currentElement = element;
94
this._rootNodeID = null;
95
this._instance = null;
96
97
// See ReactUpdateQueue
98
this._pendingElement = null;
99
this._pendingStateQueue = null;
100
this._pendingReplaceState = false;
101
this._pendingForceUpdate = false;
102
103
this._renderedComponent = null;
104
105
this._context = null;
106
this._mountOrder = 0;
107
this._isTopLevel = false;
108
109
// See ReactUpdates and ReactUpdateQueue.
110
this._pendingCallbacks = null;
111
},
112
113
/**
114
* Initializes the component, renders markup, and registers event listeners.
115
*
116
* @param {string} rootID DOM ID of the root node.
117
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
118
* @return {?string} Rendered markup to be inserted into the DOM.
119
* @final
120
* @internal
121
*/
122
mountComponent: function(rootID, transaction, context) {
123
this._context = context;
124
this._mountOrder = nextMountID++;
125
this._rootNodeID = rootID;
126
127
var publicProps = this._processProps(this._currentElement.props);
128
var publicContext = this._processContext(this._currentElement._context);
129
130
var Component = ReactNativeComponent.getComponentClassForElement(
131
this._currentElement
132
);
133
134
// Initialize the public class
135
var inst = new Component(publicProps, publicContext);
136
137
if ("production" !== process.env.NODE_ENV) {
138
// This will throw later in _renderValidatedComponent, but add an early
139
// warning now to help debugging
140
("production" !== process.env.NODE_ENV ? warning(
141
inst.render != null,
142
'%s(...): No `render` method found on the returned component ' +
143
'instance: you may have forgotten to define `render` in your ' +
144
'component or you may have accidentally tried to render an element ' +
145
'whose type is a function that isn\'t a React component.',
146
Component.displayName || Component.name || 'Component'
147
) : null);
148
}
149
150
// These should be set up in the constructor, but as a convenience for
151
// simpler class abstractions, we set them up after the fact.
152
inst.props = publicProps;
153
inst.context = publicContext;
154
inst.refs = emptyObject;
155
156
this._instance = inst;
157
158
// Store a reference from the instance back to the internal representation
159
ReactInstanceMap.set(inst, this);
160
161
if ("production" !== process.env.NODE_ENV) {
162
this._warnIfContextsDiffer(this._currentElement._context, context);
163
}
164
165
if ("production" !== process.env.NODE_ENV) {
166
// Since plain JS classes are defined without any special initialization
167
// logic, we can not catch common errors early. Therefore, we have to
168
// catch them here, at initialization time, instead.
169
("production" !== process.env.NODE_ENV ? warning(
170
!inst.getInitialState ||
171
inst.getInitialState.isReactClassApproved,
172
'getInitialState was defined on %s, a plain JavaScript class. ' +
173
'This is only supported for classes created using React.createClass. ' +
174
'Did you mean to define a state property instead?',
175
this.getName() || 'a component'
176
) : null);
177
("production" !== process.env.NODE_ENV ? warning(
178
!inst.getDefaultProps ||
179
inst.getDefaultProps.isReactClassApproved,
180
'getDefaultProps was defined on %s, a plain JavaScript class. ' +
181
'This is only supported for classes created using React.createClass. ' +
182
'Use a static property to define defaultProps instead.',
183
this.getName() || 'a component'
184
) : null);
185
("production" !== process.env.NODE_ENV ? warning(
186
!inst.propTypes,
187
'propTypes was defined as an instance property on %s. Use a static ' +
188
'property to define propTypes instead.',
189
this.getName() || 'a component'
190
) : null);
191
("production" !== process.env.NODE_ENV ? warning(
192
!inst.contextTypes,
193
'contextTypes was defined as an instance property on %s. Use a ' +
194
'static property to define contextTypes instead.',
195
this.getName() || 'a component'
196
) : null);
197
("production" !== process.env.NODE_ENV ? warning(
198
typeof inst.componentShouldUpdate !== 'function',
199
'%s has a method called ' +
200
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
201
'The name is phrased as a question because the function is ' +
202
'expected to return a value.',
203
(this.getName() || 'A component')
204
) : null);
205
}
206
207
var initialState = inst.state;
208
if (initialState === undefined) {
209
inst.state = initialState = null;
210
}
211
("production" !== process.env.NODE_ENV ? invariant(
212
typeof initialState === 'object' && !Array.isArray(initialState),
213
'%s.state: must be set to an object or null',
214
this.getName() || 'ReactCompositeComponent'
215
) : invariant(typeof initialState === 'object' && !Array.isArray(initialState)));
216
217
this._pendingStateQueue = null;
218
this._pendingReplaceState = false;
219
this._pendingForceUpdate = false;
220
221
var childContext;
222
var renderedElement;
223
224
var previouslyMounting = ReactLifeCycle.currentlyMountingInstance;
225
ReactLifeCycle.currentlyMountingInstance = this;
226
try {
227
if (inst.componentWillMount) {
228
inst.componentWillMount();
229
// When mounting, calls to `setState` by `componentWillMount` will set
230
// `this._pendingStateQueue` without triggering a re-render.
231
if (this._pendingStateQueue) {
232
inst.state = this._processPendingState(inst.props, inst.context);
233
}
234
}
235
236
childContext = this._getValidatedChildContext(context);
237
renderedElement = this._renderValidatedComponent(childContext);
238
} finally {
239
ReactLifeCycle.currentlyMountingInstance = previouslyMounting;
240
}
241
242
this._renderedComponent = this._instantiateReactComponent(
243
renderedElement,
244
this._currentElement.type // The wrapping type
245
);
246
247
var markup = ReactReconciler.mountComponent(
248
this._renderedComponent,
249
rootID,
250
transaction,
251
this._mergeChildContext(context, childContext)
252
);
253
if (inst.componentDidMount) {
254
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
255
}
256
257
return markup;
258
},
259
260
/**
261
* Releases any resources allocated by `mountComponent`.
262
*
263
* @final
264
* @internal
265
*/
266
unmountComponent: function() {
267
var inst = this._instance;
268
269
if (inst.componentWillUnmount) {
270
var previouslyUnmounting = ReactLifeCycle.currentlyUnmountingInstance;
271
ReactLifeCycle.currentlyUnmountingInstance = this;
272
try {
273
inst.componentWillUnmount();
274
} finally {
275
ReactLifeCycle.currentlyUnmountingInstance = previouslyUnmounting;
276
}
277
}
278
279
ReactReconciler.unmountComponent(this._renderedComponent);
280
this._renderedComponent = null;
281
282
// Reset pending fields
283
this._pendingStateQueue = null;
284
this._pendingReplaceState = false;
285
this._pendingForceUpdate = false;
286
this._pendingCallbacks = null;
287
this._pendingElement = null;
288
289
// These fields do not really need to be reset since this object is no
290
// longer accessible.
291
this._context = null;
292
this._rootNodeID = null;
293
294
// Delete the reference from the instance to this internal representation
295
// which allow the internals to be properly cleaned up even if the user
296
// leaks a reference to the public instance.
297
ReactInstanceMap.remove(inst);
298
299
// Some existing components rely on inst.props even after they've been
300
// destroyed (in event handlers).
301
// TODO: inst.props = null;
302
// TODO: inst.state = null;
303
// TODO: inst.context = null;
304
},
305
306
/**
307
* Schedule a partial update to the props. Only used for internal testing.
308
*
309
* @param {object} partialProps Subset of the next props.
310
* @param {?function} callback Called after props are updated.
311
* @final
312
* @internal
313
*/
314
_setPropsInternal: function(partialProps, callback) {
315
// This is a deoptimized path. We optimize for always having an element.
316
// This creates an extra internal element.
317
var element = this._pendingElement || this._currentElement;
318
this._pendingElement = ReactElement.cloneAndReplaceProps(
319
element,
320
assign({}, element.props, partialProps)
321
);
322
ReactUpdates.enqueueUpdate(this, callback);
323
},
324
325
/**
326
* Filters the context object to only contain keys specified in
327
* `contextTypes`
328
*
329
* @param {object} context
330
* @return {?object}
331
* @private
332
*/
333
_maskContext: function(context) {
334
var maskedContext = null;
335
// This really should be getting the component class for the element,
336
// but we know that we're not going to need it for built-ins.
337
if (typeof this._currentElement.type === 'string') {
338
return emptyObject;
339
}
340
var contextTypes = this._currentElement.type.contextTypes;
341
if (!contextTypes) {
342
return emptyObject;
343
}
344
maskedContext = {};
345
for (var contextName in contextTypes) {
346
maskedContext[contextName] = context[contextName];
347
}
348
return maskedContext;
349
},
350
351
/**
352
* Filters the context object to only contain keys specified in
353
* `contextTypes`, and asserts that they are valid.
354
*
355
* @param {object} context
356
* @return {?object}
357
* @private
358
*/
359
_processContext: function(context) {
360
var maskedContext = this._maskContext(context);
361
if ("production" !== process.env.NODE_ENV) {
362
var Component = ReactNativeComponent.getComponentClassForElement(
363
this._currentElement
364
);
365
if (Component.contextTypes) {
366
this._checkPropTypes(
367
Component.contextTypes,
368
maskedContext,
369
ReactPropTypeLocations.context
370
);
371
}
372
}
373
return maskedContext;
374
},
375
376
/**
377
* @param {object} currentContext
378
* @return {object}
379
* @private
380
*/
381
_getValidatedChildContext: function(currentContext) {
382
var inst = this._instance;
383
var childContext = inst.getChildContext && inst.getChildContext();
384
if (childContext) {
385
("production" !== process.env.NODE_ENV ? invariant(
386
typeof inst.constructor.childContextTypes === 'object',
387
'%s.getChildContext(): childContextTypes must be defined in order to ' +
388
'use getChildContext().',
389
this.getName() || 'ReactCompositeComponent'
390
) : invariant(typeof inst.constructor.childContextTypes === 'object'));
391
if ("production" !== process.env.NODE_ENV) {
392
this._checkPropTypes(
393
inst.constructor.childContextTypes,
394
childContext,
395
ReactPropTypeLocations.childContext
396
);
397
}
398
for (var name in childContext) {
399
("production" !== process.env.NODE_ENV ? invariant(
400
name in inst.constructor.childContextTypes,
401
'%s.getChildContext(): key "%s" is not defined in childContextTypes.',
402
this.getName() || 'ReactCompositeComponent',
403
name
404
) : invariant(name in inst.constructor.childContextTypes));
405
}
406
return childContext;
407
}
408
return null;
409
},
410
411
_mergeChildContext: function(currentContext, childContext) {
412
if (childContext) {
413
return assign({}, currentContext, childContext);
414
}
415
return currentContext;
416
},
417
418
/**
419
* Processes props by setting default values for unspecified props and
420
* asserting that the props are valid. Does not mutate its argument; returns
421
* a new props object with defaults merged in.
422
*
423
* @param {object} newProps
424
* @return {object}
425
* @private
426
*/
427
_processProps: function(newProps) {
428
if ("production" !== process.env.NODE_ENV) {
429
var Component = ReactNativeComponent.getComponentClassForElement(
430
this._currentElement
431
);
432
if (Component.propTypes) {
433
this._checkPropTypes(
434
Component.propTypes,
435
newProps,
436
ReactPropTypeLocations.prop
437
);
438
}
439
}
440
return newProps;
441
},
442
443
/**
444
* Assert that the props are valid
445
*
446
* @param {object} propTypes Map of prop name to a ReactPropType
447
* @param {object} props
448
* @param {string} location e.g. "prop", "context", "child context"
449
* @private
450
*/
451
_checkPropTypes: function(propTypes, props, location) {
452
// TODO: Stop validating prop types here and only use the element
453
// validation.
454
var componentName = this.getName();
455
for (var propName in propTypes) {
456
if (propTypes.hasOwnProperty(propName)) {
457
var error;
458
try {
459
// This is intentionally an invariant that gets caught. It's the same
460
// behavior as without this statement except with a better message.
461
("production" !== process.env.NODE_ENV ? invariant(
462
typeof propTypes[propName] === 'function',
463
'%s: %s type `%s` is invalid; it must be a function, usually ' +
464
'from React.PropTypes.',
465
componentName || 'React class',
466
ReactPropTypeLocationNames[location],
467
propName
468
) : invariant(typeof propTypes[propName] === 'function'));
469
error = propTypes[propName](props, propName, componentName, location);
470
} catch (ex) {
471
error = ex;
472
}
473
if (error instanceof Error) {
474
// We may want to extend this logic for similar errors in
475
// React.render calls, so I'm abstracting it away into
476
// a function to minimize refactoring in the future
477
var addendum = getDeclarationErrorAddendum(this);
478
479
if (location === ReactPropTypeLocations.prop) {
480
// Preface gives us something to blacklist in warning module
481
("production" !== process.env.NODE_ENV ? warning(
482
false,
483
'Failed Composite propType: %s%s',
484
error.message,
485
addendum
486
) : null);
487
} else {
488
("production" !== process.env.NODE_ENV ? warning(
489
false,
490
'Failed Context Types: %s%s',
491
error.message,
492
addendum
493
) : null);
494
}
495
}
496
}
497
}
498
},
499
500
receiveComponent: function(nextElement, transaction, nextContext) {
501
var prevElement = this._currentElement;
502
var prevContext = this._context;
503
504
this._pendingElement = null;
505
506
this.updateComponent(
507
transaction,
508
prevElement,
509
nextElement,
510
prevContext,
511
nextContext
512
);
513
},
514
515
/**
516
* If any of `_pendingElement`, `_pendingStateQueue`, or `_pendingForceUpdate`
517
* is set, update the component.
518
*
519
* @param {ReactReconcileTransaction} transaction
520
* @internal
521
*/
522
performUpdateIfNecessary: function(transaction) {
523
if (this._pendingElement != null) {
524
ReactReconciler.receiveComponent(
525
this,
526
this._pendingElement || this._currentElement,
527
transaction,
528
this._context
529
);
530
}
531
532
if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
533
if ("production" !== process.env.NODE_ENV) {
534
ReactElementValidator.checkAndWarnForMutatedProps(
535
this._currentElement
536
);
537
}
538
539
this.updateComponent(
540
transaction,
541
this._currentElement,
542
this._currentElement,
543
this._context,
544
this._context
545
);
546
}
547
},
548
549
/**
550
* Compare two contexts, warning if they are different
551
* TODO: Remove this check when owner-context is removed
552
*/
553
_warnIfContextsDiffer: function(ownerBasedContext, parentBasedContext) {
554
ownerBasedContext = this._maskContext(ownerBasedContext);
555
parentBasedContext = this._maskContext(parentBasedContext);
556
var parentKeys = Object.keys(parentBasedContext).sort();
557
var displayName = this.getName() || 'ReactCompositeComponent';
558
for (var i = 0; i < parentKeys.length; i++) {
559
var key = parentKeys[i];
560
("production" !== process.env.NODE_ENV ? warning(
561
ownerBasedContext[key] === parentBasedContext[key],
562
'owner-based and parent-based contexts differ ' +
563
'(values: `%s` vs `%s`) for key (%s) while mounting %s ' +
564
'(see: http://fb.me/react-context-by-parent)',
565
ownerBasedContext[key],
566
parentBasedContext[key],
567
key,
568
displayName
569
) : null);
570
}
571
},
572
573
/**
574
* Perform an update to a mounted component. The componentWillReceiveProps and
575
* shouldComponentUpdate methods are called, then (assuming the update isn't
576
* skipped) the remaining update lifecycle methods are called and the DOM
577
* representation is updated.
578
*
579
* By default, this implements React's rendering and reconciliation algorithm.
580
* Sophisticated clients may wish to override this.
581
*
582
* @param {ReactReconcileTransaction} transaction
583
* @param {ReactElement} prevParentElement
584
* @param {ReactElement} nextParentElement
585
* @internal
586
* @overridable
587
*/
588
updateComponent: function(
589
transaction,
590
prevParentElement,
591
nextParentElement,
592
prevUnmaskedContext,
593
nextUnmaskedContext
594
) {
595
var inst = this._instance;
596
597
var nextContext = inst.context;
598
var nextProps = inst.props;
599
600
// Distinguish between a props update versus a simple state update
601
if (prevParentElement !== nextParentElement) {
602
nextContext = this._processContext(nextParentElement._context);
603
nextProps = this._processProps(nextParentElement.props);
604
605
if ("production" !== process.env.NODE_ENV) {
606
if (nextUnmaskedContext != null) {
607
this._warnIfContextsDiffer(
608
nextParentElement._context,
609
nextUnmaskedContext
610
);
611
}
612
}
613
614
// An update here will schedule an update but immediately set
615
// _pendingStateQueue which will ensure that any state updates gets
616
// immediately reconciled instead of waiting for the next batch.
617
618
if (inst.componentWillReceiveProps) {
619
inst.componentWillReceiveProps(nextProps, nextContext);
620
}
621
}
622
623
var nextState = this._processPendingState(nextProps, nextContext);
624
625
var shouldUpdate =
626
this._pendingForceUpdate ||
627
!inst.shouldComponentUpdate ||
628
inst.shouldComponentUpdate(nextProps, nextState, nextContext);
629
630
if ("production" !== process.env.NODE_ENV) {
631
("production" !== process.env.NODE_ENV ? warning(
632
typeof shouldUpdate !== 'undefined',
633
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
634
'boolean value. Make sure to return true or false.',
635
this.getName() || 'ReactCompositeComponent'
636
) : null);
637
}
638
639
if (shouldUpdate) {
640
this._pendingForceUpdate = false;
641
// Will set `this.props`, `this.state` and `this.context`.
642
this._performComponentUpdate(
643
nextParentElement,
644
nextProps,
645
nextState,
646
nextContext,
647
transaction,
648
nextUnmaskedContext
649
);
650
} else {
651
// If it's determined that a component should not update, we still want
652
// to set props and state but we shortcut the rest of the update.
653
this._currentElement = nextParentElement;
654
this._context = nextUnmaskedContext;
655
inst.props = nextProps;
656
inst.state = nextState;
657
inst.context = nextContext;
658
}
659
},
660
661
_processPendingState: function(props, context) {
662
var inst = this._instance;
663
var queue = this._pendingStateQueue;
664
var replace = this._pendingReplaceState;
665
this._pendingReplaceState = false;
666
this._pendingStateQueue = null;
667
668
if (!queue) {
669
return inst.state;
670
}
671
672
if (replace && queue.length === 1) {
673
return queue[0];
674
}
675
676
var nextState = assign({}, replace ? queue[0] : inst.state);
677
for (var i = replace ? 1 : 0; i < queue.length; i++) {
678
var partial = queue[i];
679
assign(
680
nextState,
681
typeof partial === 'function' ?
682
partial.call(inst, nextState, props, context) :
683
partial
684
);
685
}
686
687
return nextState;
688
},
689
690
/**
691
* Merges new props and state, notifies delegate methods of update and
692
* performs update.
693
*
694
* @param {ReactElement} nextElement Next element
695
* @param {object} nextProps Next public object to set as properties.
696
* @param {?object} nextState Next object to set as state.
697
* @param {?object} nextContext Next public object to set as context.
698
* @param {ReactReconcileTransaction} transaction
699
* @param {?object} unmaskedContext
700
* @private
701
*/
702
_performComponentUpdate: function(
703
nextElement,
704
nextProps,
705
nextState,
706
nextContext,
707
transaction,
708
unmaskedContext
709
) {
710
var inst = this._instance;
711
712
var prevProps = inst.props;
713
var prevState = inst.state;
714
var prevContext = inst.context;
715
716
if (inst.componentWillUpdate) {
717
inst.componentWillUpdate(nextProps, nextState, nextContext);
718
}
719
720
this._currentElement = nextElement;
721
this._context = unmaskedContext;
722
inst.props = nextProps;
723
inst.state = nextState;
724
inst.context = nextContext;
725
726
this._updateRenderedComponent(transaction, unmaskedContext);
727
728
if (inst.componentDidUpdate) {
729
transaction.getReactMountReady().enqueue(
730
inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
731
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 childContext = this._getValidatedChildContext();
746
var nextRenderedElement = this._renderValidatedComponent(childContext);
747
if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
748
ReactReconciler.receiveComponent(
749
prevComponentInstance,
750
nextRenderedElement,
751
transaction,
752
this._mergeChildContext(context, childContext)
753
);
754
} else {
755
// These two IDs are actually the same! But nothing should rely on that.
756
var thisID = this._rootNodeID;
757
var prevComponentID = prevComponentInstance._rootNodeID;
758
ReactReconciler.unmountComponent(prevComponentInstance);
759
760
this._renderedComponent = this._instantiateReactComponent(
761
nextRenderedElement,
762
this._currentElement.type
763
);
764
var nextMarkup = ReactReconciler.mountComponent(
765
this._renderedComponent,
766
thisID,
767
transaction,
768
this._mergeChildContext(context, childContext)
769
);
770
this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup);
771
}
772
},
773
774
/**
775
* @protected
776
*/
777
_replaceNodeWithMarkupByID: function(prevComponentID, nextMarkup) {
778
ReactComponentEnvironment.replaceNodeWithMarkupByID(
779
prevComponentID,
780
nextMarkup
781
);
782
},
783
784
/**
785
* @protected
786
*/
787
_renderValidatedComponentWithoutOwnerOrContext: function() {
788
var inst = this._instance;
789
var renderedComponent = inst.render();
790
if ("production" !== process.env.NODE_ENV) {
791
// We allow auto-mocks to proceed as if they're returning null.
792
if (typeof renderedComponent === 'undefined' &&
793
inst.render._isMockFunction) {
794
// This is probably bad practice. Consider warning here and
795
// deprecating this convenience.
796
renderedComponent = null;
797
}
798
}
799
800
return renderedComponent;
801
},
802
803
/**
804
* @private
805
*/
806
_renderValidatedComponent: function(childContext) {
807
var renderedComponent;
808
var previousContext = ReactContext.current;
809
ReactContext.current = this._mergeChildContext(
810
this._currentElement._context,
811
childContext
812
);
813
ReactCurrentOwner.current = this;
814
try {
815
renderedComponent =
816
this._renderValidatedComponentWithoutOwnerOrContext();
817
} finally {
818
ReactContext.current = previousContext;
819
ReactCurrentOwner.current = null;
820
}
821
("production" !== process.env.NODE_ENV ? invariant(
822
// TODO: An `isValidNode` function would probably be more appropriate
823
renderedComponent === null || renderedComponent === false ||
824
ReactElement.isValidElement(renderedComponent),
825
'%s.render(): A valid ReactComponent must be returned. You may have ' +
826
'returned undefined, an array or some other invalid object.',
827
this.getName() || 'ReactCompositeComponent'
828
) : invariant(// TODO: An `isValidNode` function would probably be more appropriate
829
renderedComponent === null || renderedComponent === false ||
830
ReactElement.isValidElement(renderedComponent)));
831
return renderedComponent;
832
},
833
834
/**
835
* Lazily allocates the refs object and stores `component` as `ref`.
836
*
837
* @param {string} ref Reference name.
838
* @param {component} component Component to store as `ref`.
839
* @final
840
* @private
841
*/
842
attachRef: function(ref, component) {
843
var inst = this.getPublicInstance();
844
var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs;
845
refs[ref] = component.getPublicInstance();
846
},
847
848
/**
849
* Detaches a reference name.
850
*
851
* @param {string} ref Name to dereference.
852
* @final
853
* @private
854
*/
855
detachRef: function(ref) {
856
var refs = this.getPublicInstance().refs;
857
delete refs[ref];
858
},
859
860
/**
861
* Get a text description of the component that can be used to identify it
862
* in error messages.
863
* @return {string} The name or null.
864
* @internal
865
*/
866
getName: function() {
867
var type = this._currentElement.type;
868
var constructor = this._instance && this._instance.constructor;
869
return (
870
type.displayName || (constructor && constructor.displayName) ||
871
type.name || (constructor && constructor.name) ||
872
null
873
);
874
},
875
876
/**
877
* Get the publicly accessible representation of this component - i.e. what
878
* is exposed by refs and returned by React.render. Can be null for stateless
879
* components.
880
*
881
* @return {ReactComponent} the public component instance.
882
* @internal
883
*/
884
getPublicInstance: function() {
885
return this._instance;
886
},
887
888
// Stub
889
_instantiateReactComponent: null
890
891
};
892
893
ReactPerf.measureMethods(
894
ReactCompositeComponentMixin,
895
'ReactCompositeComponent',
896
{
897
mountComponent: 'mountComponent',
898
updateComponent: 'updateComponent',
899
_renderValidatedComponent: '_renderValidatedComponent'
900
}
901
);
902
903
var ReactCompositeComponent = {
904
905
Mixin: ReactCompositeComponentMixin
906
907
};
908
909
module.exports = ReactCompositeComponent;
910
911