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 ReactClass
10
*/
11
12
'use strict';
13
14
var ReactComponent = require("./ReactComponent");
15
var ReactCurrentOwner = require("./ReactCurrentOwner");
16
var ReactElement = require("./ReactElement");
17
var ReactErrorUtils = require("./ReactErrorUtils");
18
var ReactInstanceMap = require("./ReactInstanceMap");
19
var ReactLifeCycle = require("./ReactLifeCycle");
20
var ReactPropTypeLocations = require("./ReactPropTypeLocations");
21
var ReactPropTypeLocationNames = require("./ReactPropTypeLocationNames");
22
var ReactUpdateQueue = require("./ReactUpdateQueue");
23
24
var assign = require("./Object.assign");
25
var invariant = require("./invariant");
26
var keyMirror = require("./keyMirror");
27
var keyOf = require("./keyOf");
28
var warning = require("./warning");
29
30
var MIXINS_KEY = keyOf({mixins: null});
31
32
/**
33
* Policies that describe methods in `ReactClassInterface`.
34
*/
35
var SpecPolicy = keyMirror({
36
/**
37
* These methods may be defined only once by the class specification or mixin.
38
*/
39
DEFINE_ONCE: null,
40
/**
41
* These methods may be defined by both the class specification and mixins.
42
* Subsequent definitions will be chained. These methods must return void.
43
*/
44
DEFINE_MANY: null,
45
/**
46
* These methods are overriding the base class.
47
*/
48
OVERRIDE_BASE: null,
49
/**
50
* These methods are similar to DEFINE_MANY, except we assume they return
51
* objects. We try to merge the keys of the return values of all the mixed in
52
* functions. If there is a key conflict we throw.
53
*/
54
DEFINE_MANY_MERGED: null
55
});
56
57
58
var injectedMixins = [];
59
60
/**
61
* Composite components are higher-level components that compose other composite
62
* or native components.
63
*
64
* To create a new type of `ReactClass`, pass a specification of
65
* your new class to `React.createClass`. The only requirement of your class
66
* specification is that you implement a `render` method.
67
*
68
* var MyComponent = React.createClass({
69
* render: function() {
70
* return <div>Hello World</div>;
71
* }
72
* });
73
*
74
* The class specification supports a specific protocol of methods that have
75
* special meaning (e.g. `render`). See `ReactClassInterface` for
76
* more the comprehensive protocol. Any other properties and methods in the
77
* class specification will available on the prototype.
78
*
79
* @interface ReactClassInterface
80
* @internal
81
*/
82
var ReactClassInterface = {
83
84
/**
85
* An array of Mixin objects to include when defining your component.
86
*
87
* @type {array}
88
* @optional
89
*/
90
mixins: SpecPolicy.DEFINE_MANY,
91
92
/**
93
* An object containing properties and methods that should be defined on
94
* the component's constructor instead of its prototype (static methods).
95
*
96
* @type {object}
97
* @optional
98
*/
99
statics: SpecPolicy.DEFINE_MANY,
100
101
/**
102
* Definition of prop types for this component.
103
*
104
* @type {object}
105
* @optional
106
*/
107
propTypes: SpecPolicy.DEFINE_MANY,
108
109
/**
110
* Definition of context types for this component.
111
*
112
* @type {object}
113
* @optional
114
*/
115
contextTypes: SpecPolicy.DEFINE_MANY,
116
117
/**
118
* Definition of context types this component sets for its children.
119
*
120
* @type {object}
121
* @optional
122
*/
123
childContextTypes: SpecPolicy.DEFINE_MANY,
124
125
// ==== Definition methods ====
126
127
/**
128
* Invoked when the component is mounted. Values in the mapping will be set on
129
* `this.props` if that prop is not specified (i.e. using an `in` check).
130
*
131
* This method is invoked before `getInitialState` and therefore cannot rely
132
* on `this.state` or use `this.setState`.
133
*
134
* @return {object}
135
* @optional
136
*/
137
getDefaultProps: SpecPolicy.DEFINE_MANY_MERGED,
138
139
/**
140
* Invoked once before the component is mounted. The return value will be used
141
* as the initial value of `this.state`.
142
*
143
* getInitialState: function() {
144
* return {
145
* isOn: false,
146
* fooBaz: new BazFoo()
147
* }
148
* }
149
*
150
* @return {object}
151
* @optional
152
*/
153
getInitialState: SpecPolicy.DEFINE_MANY_MERGED,
154
155
/**
156
* @return {object}
157
* @optional
158
*/
159
getChildContext: SpecPolicy.DEFINE_MANY_MERGED,
160
161
/**
162
* Uses props from `this.props` and state from `this.state` to render the
163
* structure of the component.
164
*
165
* No guarantees are made about when or how often this method is invoked, so
166
* it must not have side effects.
167
*
168
* render: function() {
169
* var name = this.props.name;
170
* return <div>Hello, {name}!</div>;
171
* }
172
*
173
* @return {ReactComponent}
174
* @nosideeffects
175
* @required
176
*/
177
render: SpecPolicy.DEFINE_ONCE,
178
179
180
181
// ==== Delegate methods ====
182
183
/**
184
* Invoked when the component is initially created and about to be mounted.
185
* This may have side effects, but any external subscriptions or data created
186
* by this method must be cleaned up in `componentWillUnmount`.
187
*
188
* @optional
189
*/
190
componentWillMount: SpecPolicy.DEFINE_MANY,
191
192
/**
193
* Invoked when the component has been mounted and has a DOM representation.
194
* However, there is no guarantee that the DOM node is in the document.
195
*
196
* Use this as an opportunity to operate on the DOM when the component has
197
* been mounted (initialized and rendered) for the first time.
198
*
199
* @param {DOMElement} rootNode DOM element representing the component.
200
* @optional
201
*/
202
componentDidMount: SpecPolicy.DEFINE_MANY,
203
204
/**
205
* Invoked before the component receives new props.
206
*
207
* Use this as an opportunity to react to a prop transition by updating the
208
* state using `this.setState`. Current props are accessed via `this.props`.
209
*
210
* componentWillReceiveProps: function(nextProps, nextContext) {
211
* this.setState({
212
* likesIncreasing: nextProps.likeCount > this.props.likeCount
213
* });
214
* }
215
*
216
* NOTE: There is no equivalent `componentWillReceiveState`. An incoming prop
217
* transition may cause a state change, but the opposite is not true. If you
218
* need it, you are probably looking for `componentWillUpdate`.
219
*
220
* @param {object} nextProps
221
* @optional
222
*/
223
componentWillReceiveProps: SpecPolicy.DEFINE_MANY,
224
225
/**
226
* Invoked while deciding if the component should be updated as a result of
227
* receiving new props, state and/or context.
228
*
229
* Use this as an opportunity to `return false` when you're certain that the
230
* transition to the new props/state/context will not require a component
231
* update.
232
*
233
* shouldComponentUpdate: function(nextProps, nextState, nextContext) {
234
* return !equal(nextProps, this.props) ||
235
* !equal(nextState, this.state) ||
236
* !equal(nextContext, this.context);
237
* }
238
*
239
* @param {object} nextProps
240
* @param {?object} nextState
241
* @param {?object} nextContext
242
* @return {boolean} True if the component should update.
243
* @optional
244
*/
245
shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,
246
247
/**
248
* Invoked when the component is about to update due to a transition from
249
* `this.props`, `this.state` and `this.context` to `nextProps`, `nextState`
250
* and `nextContext`.
251
*
252
* Use this as an opportunity to perform preparation before an update occurs.
253
*
254
* NOTE: You **cannot** use `this.setState()` in this method.
255
*
256
* @param {object} nextProps
257
* @param {?object} nextState
258
* @param {?object} nextContext
259
* @param {ReactReconcileTransaction} transaction
260
* @optional
261
*/
262
componentWillUpdate: SpecPolicy.DEFINE_MANY,
263
264
/**
265
* Invoked when the component's DOM representation has been updated.
266
*
267
* Use this as an opportunity to operate on the DOM when the component has
268
* been updated.
269
*
270
* @param {object} prevProps
271
* @param {?object} prevState
272
* @param {?object} prevContext
273
* @param {DOMElement} rootNode DOM element representing the component.
274
* @optional
275
*/
276
componentDidUpdate: SpecPolicy.DEFINE_MANY,
277
278
/**
279
* Invoked when the component is about to be removed from its parent and have
280
* its DOM representation destroyed.
281
*
282
* Use this as an opportunity to deallocate any external resources.
283
*
284
* NOTE: There is no `componentDidUnmount` since your component will have been
285
* destroyed by that point.
286
*
287
* @optional
288
*/
289
componentWillUnmount: SpecPolicy.DEFINE_MANY,
290
291
292
293
// ==== Advanced methods ====
294
295
/**
296
* Updates the component's currently mounted DOM representation.
297
*
298
* By default, this implements React's rendering and reconciliation algorithm.
299
* Sophisticated clients may wish to override this.
300
*
301
* @param {ReactReconcileTransaction} transaction
302
* @internal
303
* @overridable
304
*/
305
updateComponent: SpecPolicy.OVERRIDE_BASE
306
307
};
308
309
/**
310
* Mapping from class specification keys to special processing functions.
311
*
312
* Although these are declared like instance properties in the specification
313
* when defining classes using `React.createClass`, they are actually static
314
* and are accessible on the constructor instead of the prototype. Despite
315
* being static, they must be defined outside of the "statics" key under
316
* which all other static methods are defined.
317
*/
318
var RESERVED_SPEC_KEYS = {
319
displayName: function(Constructor, displayName) {
320
Constructor.displayName = displayName;
321
},
322
mixins: function(Constructor, mixins) {
323
if (mixins) {
324
for (var i = 0; i < mixins.length; i++) {
325
mixSpecIntoComponent(Constructor, mixins[i]);
326
}
327
}
328
},
329
childContextTypes: function(Constructor, childContextTypes) {
330
if ("production" !== process.env.NODE_ENV) {
331
validateTypeDef(
332
Constructor,
333
childContextTypes,
334
ReactPropTypeLocations.childContext
335
);
336
}
337
Constructor.childContextTypes = assign(
338
{},
339
Constructor.childContextTypes,
340
childContextTypes
341
);
342
},
343
contextTypes: function(Constructor, contextTypes) {
344
if ("production" !== process.env.NODE_ENV) {
345
validateTypeDef(
346
Constructor,
347
contextTypes,
348
ReactPropTypeLocations.context
349
);
350
}
351
Constructor.contextTypes = assign(
352
{},
353
Constructor.contextTypes,
354
contextTypes
355
);
356
},
357
/**
358
* Special case getDefaultProps which should move into statics but requires
359
* automatic merging.
360
*/
361
getDefaultProps: function(Constructor, getDefaultProps) {
362
if (Constructor.getDefaultProps) {
363
Constructor.getDefaultProps = createMergedResultFunction(
364
Constructor.getDefaultProps,
365
getDefaultProps
366
);
367
} else {
368
Constructor.getDefaultProps = getDefaultProps;
369
}
370
},
371
propTypes: function(Constructor, propTypes) {
372
if ("production" !== process.env.NODE_ENV) {
373
validateTypeDef(
374
Constructor,
375
propTypes,
376
ReactPropTypeLocations.prop
377
);
378
}
379
Constructor.propTypes = assign(
380
{},
381
Constructor.propTypes,
382
propTypes
383
);
384
},
385
statics: function(Constructor, statics) {
386
mixStaticSpecIntoComponent(Constructor, statics);
387
}
388
};
389
390
function validateTypeDef(Constructor, typeDef, location) {
391
for (var propName in typeDef) {
392
if (typeDef.hasOwnProperty(propName)) {
393
// use a warning instead of an invariant so components
394
// don't show up in prod but not in __DEV__
395
("production" !== process.env.NODE_ENV ? warning(
396
typeof typeDef[propName] === 'function',
397
'%s: %s type `%s` is invalid; it must be a function, usually from ' +
398
'React.PropTypes.',
399
Constructor.displayName || 'ReactClass',
400
ReactPropTypeLocationNames[location],
401
propName
402
) : null);
403
}
404
}
405
}
406
407
function validateMethodOverride(proto, name) {
408
var specPolicy = ReactClassInterface.hasOwnProperty(name) ?
409
ReactClassInterface[name] :
410
null;
411
412
// Disallow overriding of base class methods unless explicitly allowed.
413
if (ReactClassMixin.hasOwnProperty(name)) {
414
("production" !== process.env.NODE_ENV ? invariant(
415
specPolicy === SpecPolicy.OVERRIDE_BASE,
416
'ReactClassInterface: You are attempting to override ' +
417
'`%s` from your class specification. Ensure that your method names ' +
418
'do not overlap with React methods.',
419
name
420
) : invariant(specPolicy === SpecPolicy.OVERRIDE_BASE));
421
}
422
423
// Disallow defining methods more than once unless explicitly allowed.
424
if (proto.hasOwnProperty(name)) {
425
("production" !== process.env.NODE_ENV ? invariant(
426
specPolicy === SpecPolicy.DEFINE_MANY ||
427
specPolicy === SpecPolicy.DEFINE_MANY_MERGED,
428
'ReactClassInterface: You are attempting to define ' +
429
'`%s` on your component more than once. This conflict may be due ' +
430
'to a mixin.',
431
name
432
) : invariant(specPolicy === SpecPolicy.DEFINE_MANY ||
433
specPolicy === SpecPolicy.DEFINE_MANY_MERGED));
434
}
435
}
436
437
/**
438
* Mixin helper which handles policy validation and reserved
439
* specification keys when building React classses.
440
*/
441
function mixSpecIntoComponent(Constructor, spec) {
442
if (!spec) {
443
return;
444
}
445
446
("production" !== process.env.NODE_ENV ? invariant(
447
typeof spec !== 'function',
448
'ReactClass: You\'re attempting to ' +
449
'use a component class as a mixin. Instead, just use a regular object.'
450
) : invariant(typeof spec !== 'function'));
451
("production" !== process.env.NODE_ENV ? invariant(
452
!ReactElement.isValidElement(spec),
453
'ReactClass: You\'re attempting to ' +
454
'use a component as a mixin. Instead, just use a regular object.'
455
) : invariant(!ReactElement.isValidElement(spec)));
456
457
var proto = Constructor.prototype;
458
459
// By handling mixins before any other properties, we ensure the same
460
// chaining order is applied to methods with DEFINE_MANY policy, whether
461
// mixins are listed before or after these methods in the spec.
462
if (spec.hasOwnProperty(MIXINS_KEY)) {
463
RESERVED_SPEC_KEYS.mixins(Constructor, spec.mixins);
464
}
465
466
for (var name in spec) {
467
if (!spec.hasOwnProperty(name)) {
468
continue;
469
}
470
471
if (name === MIXINS_KEY) {
472
// We have already handled mixins in a special case above
473
continue;
474
}
475
476
var property = spec[name];
477
validateMethodOverride(proto, name);
478
479
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
480
RESERVED_SPEC_KEYS[name](Constructor, property);
481
} else {
482
// Setup methods on prototype:
483
// The following member methods should not be automatically bound:
484
// 1. Expected ReactClass methods (in the "interface").
485
// 2. Overridden methods (that were mixed in).
486
var isReactClassMethod =
487
ReactClassInterface.hasOwnProperty(name);
488
var isAlreadyDefined = proto.hasOwnProperty(name);
489
var markedDontBind = property && property.__reactDontBind;
490
var isFunction = typeof property === 'function';
491
var shouldAutoBind =
492
isFunction &&
493
!isReactClassMethod &&
494
!isAlreadyDefined &&
495
!markedDontBind;
496
497
if (shouldAutoBind) {
498
if (!proto.__reactAutoBindMap) {
499
proto.__reactAutoBindMap = {};
500
}
501
proto.__reactAutoBindMap[name] = property;
502
proto[name] = property;
503
} else {
504
if (isAlreadyDefined) {
505
var specPolicy = ReactClassInterface[name];
506
507
// These cases should already be caught by validateMethodOverride
508
("production" !== process.env.NODE_ENV ? invariant(
509
isReactClassMethod && (
510
(specPolicy === SpecPolicy.DEFINE_MANY_MERGED || specPolicy === SpecPolicy.DEFINE_MANY)
511
),
512
'ReactClass: Unexpected spec policy %s for key %s ' +
513
'when mixing in component specs.',
514
specPolicy,
515
name
516
) : invariant(isReactClassMethod && (
517
(specPolicy === SpecPolicy.DEFINE_MANY_MERGED || specPolicy === SpecPolicy.DEFINE_MANY)
518
)));
519
520
// For methods which are defined more than once, call the existing
521
// methods before calling the new property, merging if appropriate.
522
if (specPolicy === SpecPolicy.DEFINE_MANY_MERGED) {
523
proto[name] = createMergedResultFunction(proto[name], property);
524
} else if (specPolicy === SpecPolicy.DEFINE_MANY) {
525
proto[name] = createChainedFunction(proto[name], property);
526
}
527
} else {
528
proto[name] = property;
529
if ("production" !== process.env.NODE_ENV) {
530
// Add verbose displayName to the function, which helps when looking
531
// at profiling tools.
532
if (typeof property === 'function' && spec.displayName) {
533
proto[name].displayName = spec.displayName + '_' + name;
534
}
535
}
536
}
537
}
538
}
539
}
540
}
541
542
function mixStaticSpecIntoComponent(Constructor, statics) {
543
if (!statics) {
544
return;
545
}
546
for (var name in statics) {
547
var property = statics[name];
548
if (!statics.hasOwnProperty(name)) {
549
continue;
550
}
551
552
var isReserved = name in RESERVED_SPEC_KEYS;
553
("production" !== process.env.NODE_ENV ? invariant(
554
!isReserved,
555
'ReactClass: You are attempting to define a reserved ' +
556
'property, `%s`, that shouldn\'t be on the "statics" key. Define it ' +
557
'as an instance property instead; it will still be accessible on the ' +
558
'constructor.',
559
name
560
) : invariant(!isReserved));
561
562
var isInherited = name in Constructor;
563
("production" !== process.env.NODE_ENV ? invariant(
564
!isInherited,
565
'ReactClass: You are attempting to define ' +
566
'`%s` on your component more than once. This conflict may be ' +
567
'due to a mixin.',
568
name
569
) : invariant(!isInherited));
570
Constructor[name] = property;
571
}
572
}
573
574
/**
575
* Merge two objects, but throw if both contain the same key.
576
*
577
* @param {object} one The first object, which is mutated.
578
* @param {object} two The second object
579
* @return {object} one after it has been mutated to contain everything in two.
580
*/
581
function mergeIntoWithNoDuplicateKeys(one, two) {
582
("production" !== process.env.NODE_ENV ? invariant(
583
one && two && typeof one === 'object' && typeof two === 'object',
584
'mergeIntoWithNoDuplicateKeys(): Cannot merge non-objects.'
585
) : invariant(one && two && typeof one === 'object' && typeof two === 'object'));
586
587
for (var key in two) {
588
if (two.hasOwnProperty(key)) {
589
("production" !== process.env.NODE_ENV ? invariant(
590
one[key] === undefined,
591
'mergeIntoWithNoDuplicateKeys(): ' +
592
'Tried to merge two objects with the same key: `%s`. This conflict ' +
593
'may be due to a mixin; in particular, this may be caused by two ' +
594
'getInitialState() or getDefaultProps() methods returning objects ' +
595
'with clashing keys.',
596
key
597
) : invariant(one[key] === undefined));
598
one[key] = two[key];
599
}
600
}
601
return one;
602
}
603
604
/**
605
* Creates a function that invokes two functions and merges their return values.
606
*
607
* @param {function} one Function to invoke first.
608
* @param {function} two Function to invoke second.
609
* @return {function} Function that invokes the two argument functions.
610
* @private
611
*/
612
function createMergedResultFunction(one, two) {
613
return function mergedResult() {
614
var a = one.apply(this, arguments);
615
var b = two.apply(this, arguments);
616
if (a == null) {
617
return b;
618
} else if (b == null) {
619
return a;
620
}
621
var c = {};
622
mergeIntoWithNoDuplicateKeys(c, a);
623
mergeIntoWithNoDuplicateKeys(c, b);
624
return c;
625
};
626
}
627
628
/**
629
* Creates a function that invokes two functions and ignores their return vales.
630
*
631
* @param {function} one Function to invoke first.
632
* @param {function} two Function to invoke second.
633
* @return {function} Function that invokes the two argument functions.
634
* @private
635
*/
636
function createChainedFunction(one, two) {
637
return function chainedFunction() {
638
one.apply(this, arguments);
639
two.apply(this, arguments);
640
};
641
}
642
643
/**
644
* Binds a method to the component.
645
*
646
* @param {object} component Component whose method is going to be bound.
647
* @param {function} method Method to be bound.
648
* @return {function} The bound method.
649
*/
650
function bindAutoBindMethod(component, method) {
651
var boundMethod = method.bind(component);
652
if ("production" !== process.env.NODE_ENV) {
653
boundMethod.__reactBoundContext = component;
654
boundMethod.__reactBoundMethod = method;
655
boundMethod.__reactBoundArguments = null;
656
var componentName = component.constructor.displayName;
657
var _bind = boundMethod.bind;
658
/* eslint-disable block-scoped-var, no-undef */
659
boundMethod.bind = function(newThis ) {for (var args=[],$__0=1,$__1=arguments.length;$__0<$__1;$__0++) args.push(arguments[$__0]);
660
// User is trying to bind() an autobound method; we effectively will
661
// ignore the value of "this" that the user is trying to use, so
662
// let's warn.
663
if (newThis !== component && newThis !== null) {
664
("production" !== process.env.NODE_ENV ? warning(
665
false,
666
'bind(): React component methods may only be bound to the ' +
667
'component instance. See %s',
668
componentName
669
) : null);
670
} else if (!args.length) {
671
("production" !== process.env.NODE_ENV ? warning(
672
false,
673
'bind(): You are binding a component method to the component. ' +
674
'React does this for you automatically in a high-performance ' +
675
'way, so you can safely remove this call. See %s',
676
componentName
677
) : null);
678
return boundMethod;
679
}
680
var reboundMethod = _bind.apply(boundMethod, arguments);
681
reboundMethod.__reactBoundContext = component;
682
reboundMethod.__reactBoundMethod = method;
683
reboundMethod.__reactBoundArguments = args;
684
return reboundMethod;
685
/* eslint-enable */
686
};
687
}
688
return boundMethod;
689
}
690
691
/**
692
* Binds all auto-bound methods in a component.
693
*
694
* @param {object} component Component whose method is going to be bound.
695
*/
696
function bindAutoBindMethods(component) {
697
for (var autoBindKey in component.__reactAutoBindMap) {
698
if (component.__reactAutoBindMap.hasOwnProperty(autoBindKey)) {
699
var method = component.__reactAutoBindMap[autoBindKey];
700
component[autoBindKey] = bindAutoBindMethod(
701
component,
702
ReactErrorUtils.guard(
703
method,
704
component.constructor.displayName + '.' + autoBindKey
705
)
706
);
707
}
708
}
709
}
710
711
var typeDeprecationDescriptor = {
712
enumerable: false,
713
get: function() {
714
var displayName = this.displayName || this.name || 'Component';
715
("production" !== process.env.NODE_ENV ? warning(
716
false,
717
'%s.type is deprecated. Use %s directly to access the class.',
718
displayName,
719
displayName
720
) : null);
721
Object.defineProperty(this, 'type', {
722
value: this
723
});
724
return this;
725
}
726
};
727
728
/**
729
* Add more to the ReactClass base class. These are all legacy features and
730
* therefore not already part of the modern ReactComponent.
731
*/
732
var ReactClassMixin = {
733
734
/**
735
* TODO: This will be deprecated because state should always keep a consistent
736
* type signature and the only use case for this, is to avoid that.
737
*/
738
replaceState: function(newState, callback) {
739
ReactUpdateQueue.enqueueReplaceState(this, newState);
740
if (callback) {
741
ReactUpdateQueue.enqueueCallback(this, callback);
742
}
743
},
744
745
/**
746
* Checks whether or not this composite component is mounted.
747
* @return {boolean} True if mounted, false otherwise.
748
* @protected
749
* @final
750
*/
751
isMounted: function() {
752
if ("production" !== process.env.NODE_ENV) {
753
var owner = ReactCurrentOwner.current;
754
if (owner !== null) {
755
("production" !== process.env.NODE_ENV ? warning(
756
owner._warnedAboutRefsInRender,
757
'%s is accessing isMounted inside its render() function. ' +
758
'render() should be a pure function of props and state. It should ' +
759
'never access something that requires stale data from the previous ' +
760
'render, such as refs. Move this logic to componentDidMount and ' +
761
'componentDidUpdate instead.',
762
owner.getName() || 'A component'
763
) : null);
764
owner._warnedAboutRefsInRender = true;
765
}
766
}
767
var internalInstance = ReactInstanceMap.get(this);
768
return (
769
internalInstance &&
770
internalInstance !== ReactLifeCycle.currentlyMountingInstance
771
);
772
},
773
774
/**
775
* Sets a subset of the props.
776
*
777
* @param {object} partialProps Subset of the next props.
778
* @param {?function} callback Called after props are updated.
779
* @final
780
* @public
781
* @deprecated
782
*/
783
setProps: function(partialProps, callback) {
784
ReactUpdateQueue.enqueueSetProps(this, partialProps);
785
if (callback) {
786
ReactUpdateQueue.enqueueCallback(this, callback);
787
}
788
},
789
790
/**
791
* Replace all the props.
792
*
793
* @param {object} newProps Subset of the next props.
794
* @param {?function} callback Called after props are updated.
795
* @final
796
* @public
797
* @deprecated
798
*/
799
replaceProps: function(newProps, callback) {
800
ReactUpdateQueue.enqueueReplaceProps(this, newProps);
801
if (callback) {
802
ReactUpdateQueue.enqueueCallback(this, callback);
803
}
804
}
805
};
806
807
var ReactClassComponent = function() {};
808
assign(
809
ReactClassComponent.prototype,
810
ReactComponent.prototype,
811
ReactClassMixin
812
);
813
814
/**
815
* Module for creating composite components.
816
*
817
* @class ReactClass
818
*/
819
var ReactClass = {
820
821
/**
822
* Creates a composite component class given a class specification.
823
*
824
* @param {object} spec Class specification (which must define `render`).
825
* @return {function} Component constructor function.
826
* @public
827
*/
828
createClass: function(spec) {
829
var Constructor = function(props, context) {
830
// This constructor is overridden by mocks. The argument is used
831
// by mocks to assert on what gets mounted.
832
833
if ("production" !== process.env.NODE_ENV) {
834
("production" !== process.env.NODE_ENV ? warning(
835
this instanceof Constructor,
836
'Something is calling a React component directly. Use a factory or ' +
837
'JSX instead. See: https://fb.me/react-legacyfactory'
838
) : null);
839
}
840
841
// Wire up auto-binding
842
if (this.__reactAutoBindMap) {
843
bindAutoBindMethods(this);
844
}
845
846
this.props = props;
847
this.context = context;
848
this.state = null;
849
850
// ReactClasses doesn't have constructors. Instead, they use the
851
// getInitialState and componentWillMount methods for initialization.
852
853
var initialState = this.getInitialState ? this.getInitialState() : null;
854
if ("production" !== process.env.NODE_ENV) {
855
// We allow auto-mocks to proceed as if they're returning null.
856
if (typeof initialState === 'undefined' &&
857
this.getInitialState._isMockFunction) {
858
// This is probably bad practice. Consider warning here and
859
// deprecating this convenience.
860
initialState = null;
861
}
862
}
863
("production" !== process.env.NODE_ENV ? invariant(
864
typeof initialState === 'object' && !Array.isArray(initialState),
865
'%s.getInitialState(): must return an object or null',
866
Constructor.displayName || 'ReactCompositeComponent'
867
) : invariant(typeof initialState === 'object' && !Array.isArray(initialState)));
868
869
this.state = initialState;
870
};
871
Constructor.prototype = new ReactClassComponent();
872
Constructor.prototype.constructor = Constructor;
873
874
injectedMixins.forEach(
875
mixSpecIntoComponent.bind(null, Constructor)
876
);
877
878
mixSpecIntoComponent(Constructor, spec);
879
880
// Initialize the defaultProps property after all mixins have been merged
881
if (Constructor.getDefaultProps) {
882
Constructor.defaultProps = Constructor.getDefaultProps();
883
}
884
885
if ("production" !== process.env.NODE_ENV) {
886
// This is a tag to indicate that the use of these method names is ok,
887
// since it's used with createClass. If it's not, then it's likely a
888
// mistake so we'll warn you to use the static property, property
889
// initializer or constructor respectively.
890
if (Constructor.getDefaultProps) {
891
Constructor.getDefaultProps.isReactClassApproved = {};
892
}
893
if (Constructor.prototype.getInitialState) {
894
Constructor.prototype.getInitialState.isReactClassApproved = {};
895
}
896
}
897
898
("production" !== process.env.NODE_ENV ? invariant(
899
Constructor.prototype.render,
900
'createClass(...): Class specification must implement a `render` method.'
901
) : invariant(Constructor.prototype.render));
902
903
if ("production" !== process.env.NODE_ENV) {
904
("production" !== process.env.NODE_ENV ? warning(
905
!Constructor.prototype.componentShouldUpdate,
906
'%s has a method called ' +
907
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
908
'The name is phrased as a question because the function is ' +
909
'expected to return a value.',
910
spec.displayName || 'A component'
911
) : null);
912
}
913
914
// Reduce time spent doing lookups by setting these on the prototype.
915
for (var methodName in ReactClassInterface) {
916
if (!Constructor.prototype[methodName]) {
917
Constructor.prototype[methodName] = null;
918
}
919
}
920
921
// Legacy hook
922
Constructor.type = Constructor;
923
if ("production" !== process.env.NODE_ENV) {
924
try {
925
Object.defineProperty(Constructor, 'type', typeDeprecationDescriptor);
926
} catch (x) {
927
// IE will fail on defineProperty (es5-shim/sham too)
928
}
929
}
930
931
return Constructor;
932
},
933
934
injection: {
935
injectMixin: function(mixin) {
936
injectedMixins.push(mixin);
937
}
938
}
939
940
};
941
942
module.exports = ReactClass;
943
944