Remove support for putting mixins, propTypes, etc. in 'statics'
This leaves statics as a way to define static methods and properties that will be accessible on the return value of `React.createClass` without changing the current spec.
Marshall Roch committed
Dec 4, 2013 at 17:47 UTC
d17bd176a2dd69b7f94b3cd0aa5b552a7fd239e8
2 files changed
+114
-163
src/core/ReactCompositeComponent.js
+71
-88
@@ -85,39 +85,46 @@ var SpecPolicy = keyMirror({
85
*/
86
var ReactCompositeComponentInterface = {
87
88
- statics: {
89
- /**
90
- * An array of Mixin objects to include when defining your component.
91
- *
92
- * @type {array}
93
- * @optional
94
- */
95
- mixins: SpecPolicy.DEFINE_MANY,
96
-
97
- /**
98
- * Definition of prop types for this component.
99
- *
100
- * @type {object}
101
- * @optional
102
- */
103
- propTypes: SpecPolicy.DEFINE_MANY_MERGED,
104
-
105
- /**
106
- * Definition of context types for this component.
107
- *
108
- * @type {object}
109
- * @optional
110
- */
111
- contextTypes: SpecPolicy.DEFINE_MANY_MERGED,
112
-
113
- /**
114
- * Definition of context types this component sets for its children.
115
- *
116
- * @type {object}
117
- * @optional
118
- */
119
- childContextTypes: SpecPolicy.DEFINE_MANY_MERGED
120
- },
88
+ /**
89
+ * An array of Mixin objects to include when defining your component.
90
+ *
91
+ * @type {array}
92
+ * @optional
93
+ */
94
+ mixins: SpecPolicy.DEFINE_MANY,
95
+
96
+ /**
97
+ * An object containing properties and methods that should be defined on
98
+ * the component's constructor instead of its prototype (static methods).
99
+ *
100
+ * @type {object}
101
+ * @optional
102
+ */
103
+ statics: SpecPolicy.DEFINE_MANY_MERGED,
104
+
105
+ /**
106
+ * Definition of prop types for this component.
107
+ *
108
+ * @type {object}
109
+ * @optional
110
+ */
111
+ propTypes: SpecPolicy.DEFINE_MANY_MERGED,
112
+
113
+ /**
114
+ * Definition of context types for this component.
115
+ *
116
+ * @type {object}
117
+ * @optional
118
+ */
119
+ contextTypes: SpecPolicy.DEFINE_MANY_MERGED,
120
+
121
+ /**
122
+ * Definition of context types this component sets for its children.
123
+ *
124
+ * @type {object}
125
+ * @optional
126
+ */
127
+ childContextTypes: SpecPolicy.DEFINE_MANY_MERGED,
128
129
// ==== Definition methods ====
130
@@ -304,21 +311,18 @@ var ReactCompositeComponentInterface = {
311
};
312
313
/**
307
- * Mapping from class specification static keys to special processing functions.
308
- *
309
- * These are all defined inside the "statics" key in the class spec:
310
- *
311
- * React.createClass({
312
- * statics: {
313
- * mixins: [FooMixin]
314
- * },
315
- * render: function() {...}
316
- * })
314
+ * Mapping from class specification keys to special processing functions.
315
*
318
- * Although these are declared in the specification when defining classes
319
- * using `React.createClass`, they will not be on the component's prototype.
316
+ * Although these are declared like instance properties in the specification
317
+ * when defining classes using `React.createClass`, they are actually static
318
+ * and are accessible on the constructor instead of the prototype. Despite
319
+ * being static, they must be defined outside of the "statics" key under
320
+ * which all other static methods are defined.
321
*/
321
-var RESERVED_STATIC_SPEC_KEYS = {
322
+var RESERVED_SPEC_KEYS = {
323
+ displayName: function(ConvenienceConstructor, displayName) {
324
+ ConvenienceConstructor.componentConstructor.displayName = displayName;
325
+ },
326
mixins: function(ConvenienceConstructor, mixins) {
327
if (mixins) {
328
for (var i = 0; i < mixins.length; i++) {
@@ -352,22 +356,7 @@ var RESERVED_STATIC_SPEC_KEYS = {
356
ReactPropTypeLocations.prop
357
);
358
Constructor.propTypes = propTypes;
355
- }
356
-};
357
-
358
-/**
359
- * Mapping from class specification keys to special processing functions.
360
- *
361
- * Although these are declared in the specification when defining classes
362
- * using `React.createClass`, they will not be on the component's prototype.
363
- */
364
-var RESERVED_SPEC_KEYS = {
365
- // TODO: move this to RESERVED_STATIC_SPEC_KEYS, but this requires also
366
- // changing the JSX transform.
367
- displayName: function(ConvenienceConstructor, displayName) {
368
- ConvenienceConstructor.componentConstructor.displayName = displayName;
359
},
370
-
360
statics: function(ConvenienceConstructor, statics) {
361
mixStaticSpecIntoComponent(ConvenienceConstructor, statics);
362
}
@@ -388,13 +377,11 @@ function validateTypeDef(Constructor, typeDef, location) {
377
}
378
}
379
391
-function validateMethodOverride(proto, name, isStatic) {
392
- var specPolicy = isStatic ?
393
- ReactCompositeComponentInterface.statics[name] :
394
- ReactCompositeComponentInterface[name];
380
+function validateMethodOverride(proto, name) {
381
+ var specPolicy = ReactCompositeComponentInterface[name];
382
383
// Disallow overriding of base class methods unless explicitly allowed.
397
- if (!isStatic && ReactCompositeComponentMixin.hasOwnProperty(name)) {
384
+ if (ReactCompositeComponentMixin.hasOwnProperty(name)) {
385
invariant(
386
specPolicy === SpecPolicy.OVERRIDE_BASE,
387
'ReactCompositeComponentInterface: You are attempting to override ' +
@@ -448,13 +435,10 @@ function mixSpecIntoComponent(ConvenienceConstructor, spec) {
435
continue;
436
}
437
451
- validateMethodOverride(proto, name, false);
438
+ validateMethodOverride(proto, name);
439
440
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
441
RESERVED_SPEC_KEYS[name](ConvenienceConstructor, property);
455
- } else if (RESERVED_STATIC_SPEC_KEYS.hasOwnProperty(name)) {
456
- // TODO: deprecate this with a warning
457
- RESERVED_STATIC_SPEC_KEYS[name](ConvenienceConstructor, property);
442
} else {
443
// Setup methods on prototype:
444
// The following member methods should not be automatically bound:
@@ -504,25 +488,24 @@ function mixStaticSpecIntoComponent(ConvenienceConstructor, statics) {
488
return;
489
}
490
507
- validateMethodOverride(ConvenienceConstructor, name, true);
508
-
509
- if (RESERVED_STATIC_SPEC_KEYS.hasOwnProperty(name)) {
510
- RESERVED_STATIC_SPEC_KEYS[name](ConvenienceConstructor, property);
511
- } else {
512
- var isInherited = name in ConvenienceConstructor;
513
- var result = property;
514
- if (isInherited) {
515
- var existingProperty = ConvenienceConstructor[name];
516
- if (ReactCompositeComponentInterface.statics[name] ===
517
- SpecPolicy.DEFINE_MANY_MERGED) {
518
- result = createMergedResultFunction(existingProperty, property);
519
- } else {
520
- result = createChainedFunction(existingProperty, property);
521
- }
522
- }
523
- ConvenienceConstructor[name] = result;
524
- ConvenienceConstructor.componentConstructor[name] = result;
491
+ var isInherited = name in ConvenienceConstructor;
492
+ var result = property;
493
+ if (isInherited) {
494
+ var existingProperty = ConvenienceConstructor[name];
495
+ var existingType = typeof existingProperty;
496
+ var propertyType = typeof property;
497
+ invariant(
498
+ existingType === 'function' && propertyType === 'function',
499
+ 'ReactCompositeComponent: You are attempting to define ' +
500
+ '`%s` on your component more than once, but that is only supported ' +
501
+ 'for functions, which are chained together. This conflict may be ' +
502
+ 'due to a mixin.',
503
+ name
504
+ );
505
+ result = createChainedFunction(existingProperty, property);
506
}
507
+ ConvenienceConstructor[name] = result;
508
+ ConvenienceConstructor.componentConstructor[name] = result;
509
}
510
}
511
src/core/__tests__/ReactCompositeComponent-test.js
+43
-75
@@ -235,9 +235,7 @@ describe('ReactCompositeComponent', function() {
235
236
it('should normalize props with default values', function() {
237
var Component = React.createClass({
238
- statics: {
239
- propTypes: {key: ReactPropTypes.string.isRequired}
240
- },
238
+ propTypes: {key: ReactPropTypes.string.isRequired},
239
getDefaultProps: function() {
240
return {key: 'testKey'};
241
},
@@ -264,9 +262,7 @@ describe('ReactCompositeComponent', function() {
262
263
it('should check default prop values', function() {
264
var Component = React.createClass({
267
- statics: {
268
- propTypes: {key: ReactPropTypes.string.isRequired}
269
- },
265
+ propTypes: {key: ReactPropTypes.string.isRequired},
266
getDefaultProps: function() {
267
return {key: null};
268
},
@@ -286,10 +282,8 @@ describe('ReactCompositeComponent', function() {
282
283
it('should check declared prop types', function() {
284
var Component = React.createClass({
289
- statics: {
290
- propTypes: {
291
- key: ReactPropTypes.string.isRequired
292
- }
285
+ propTypes: {
286
+ key: ReactPropTypes.string.isRequired
287
},
288
render: function() {
289
return <span>{this.props.key}</span>;
@@ -319,10 +313,8 @@ describe('ReactCompositeComponent', function() {
313
expect(function() {
314
React.createClass({
315
displayName: 'Component',
322
- statics: {
323
- propTypes: {
324
- key: null
325
- }
316
+ propTypes: {
317
+ key: null
318
},
319
render: function() {
320
return <span>{this.props.key}</span>;
@@ -338,10 +330,8 @@ describe('ReactCompositeComponent', function() {
330
expect(function() {
331
React.createClass({
332
displayName: 'Component',
341
- statics: {
342
- contextTypes: {
343
- key: null
344
- }
333
+ contextTypes: {
334
+ key: null
335
},
336
render: function() {
337
return <span>{this.props.key}</span>;
@@ -357,10 +347,8 @@ describe('ReactCompositeComponent', function() {
347
expect(function() {
348
React.createClass({
349
displayName: 'Component',
360
- statics: {
361
- childContextTypes: {
362
- key: null
363
- }
350
+ childContextTypes: {
351
+ key: null
352
},
353
render: function() {
354
return <span>{this.props.key}</span>;
@@ -428,9 +416,7 @@ describe('ReactCompositeComponent', function() {
416
}
417
};
418
var Component = React.createClass({
431
- statics: {
432
- mixins: [Mixin]
433
- },
419
+ mixins: [Mixin],
420
getInitialState: function() {
421
return {component: true};
422
},
@@ -451,9 +437,7 @@ describe('ReactCompositeComponent', function() {
437
}
438
};
439
var Component = React.createClass({
454
- statics: {
455
- mixins: [Mixin]
456
- },
440
+ mixins: [Mixin],
441
getInitialState: function() {
442
return {x: true};
443
},
@@ -477,9 +461,7 @@ describe('ReactCompositeComponent', function() {
461
}
462
};
463
var Component = React.createClass({
480
- statics: {
481
- mixins: [Mixin]
482
- },
464
+ mixins: [Mixin],
465
getInitialState: function() {
466
return {x: true};
467
},
@@ -614,11 +596,9 @@ describe('ReactCompositeComponent', function() {
596
var grandchildInstance = null;
597
598
var Parent = React.createClass({
617
- statics: {
618
- childContextTypes: {
619
- foo: ReactPropTypes.string,
620
- depth: ReactPropTypes.number
621
- }
599
+ childContextTypes: {
600
+ foo: ReactPropTypes.string,
601
+ depth: ReactPropTypes.number
602
},
603
604
getChildContext: function() {
@@ -635,15 +615,13 @@ describe('ReactCompositeComponent', function() {
615
});
616
617
var Child = React.createClass({
638
- statics: {
639
- contextTypes: {
640
- foo: ReactPropTypes.string,
641
- depth: ReactPropTypes.number
642
- },
618
+ contextTypes: {
619
+ foo: ReactPropTypes.string,
620
+ depth: ReactPropTypes.number
621
+ },
622
644
- childContextTypes: {
645
- depth: ReactPropTypes.number
646
- }
623
+ childContextTypes: {
624
+ depth: ReactPropTypes.number
625
},
626
627
getChildContext: function() {
@@ -659,11 +637,9 @@ describe('ReactCompositeComponent', function() {
637
});
638
639
var Grandchild = React.createClass({
662
- statics: {
663
- contextTypes: {
664
- foo: ReactPropTypes.string,
665
- depth: ReactPropTypes.number
666
- }
640
+ contextTypes: {
641
+ foo: ReactPropTypes.string,
642
+ depth: ReactPropTypes.number
643
},
644
645
render: function() {
@@ -679,10 +655,8 @@ describe('ReactCompositeComponent', function() {
655
656
it('should check context types', function() {
657
var Component = React.createClass({
682
- statics: {
683
- contextTypes: {
684
- foo: ReactPropTypes.string.isRequired
685
- }
658
+ contextTypes: {
659
+ foo: ReactPropTypes.string.isRequired
660
},
661
662
render: function() {
@@ -715,11 +689,9 @@ describe('ReactCompositeComponent', function() {
689
690
it('should check child context types', function() {
691
var Component = React.createClass({
718
- statics: {
719
- childContextTypes: {
720
- foo: ReactPropTypes.string.isRequired,
721
- bar: ReactPropTypes.number
722
- }
692
+ childContextTypes: {
693
+ foo: ReactPropTypes.string.isRequired,
694
+ bar: ReactPropTypes.number
695
},
696
697
getChildContext: function() {
@@ -764,10 +736,8 @@ describe('ReactCompositeComponent', function() {
736
737
it('should filter out context not in contextTypes', function() {
738
var Component = React.createClass({
767
- statics: {
768
- contextTypes: {
769
- foo: ReactPropTypes.string
770
- }
739
+ contextTypes: {
740
+ foo: ReactPropTypes.string
741
},
742
743
render: function() {
@@ -789,11 +759,9 @@ describe('ReactCompositeComponent', function() {
759
var actualComponentDidUpdate;
760
761
var Parent = React.createClass({
792
- statics: {
793
- childContextTypes: {
794
- foo: ReactPropTypes.string.isRequired,
795
- bar: ReactPropTypes.string.isRequired
796
- }
762
+ childContextTypes: {
763
+ foo: ReactPropTypes.string.isRequired,
764
+ bar: ReactPropTypes.string.isRequired
765
},
766
767
getChildContext: function() {
@@ -809,10 +777,8 @@ describe('ReactCompositeComponent', function() {
777
});
778
779
var Component = React.createClass({
812
- statics: {
813
- contextTypes: {
814
- foo: ReactPropTypes.string
815
- }
780
+ contextTypes: {
781
+ foo: ReactPropTypes.string
782
},
783
784
componentWillReceiveProps: function(nextProps, nextContext) {
@@ -896,8 +862,9 @@ describe('ReactCompositeComponent', function() {
862
}
863
};
864
React.createClass({
865
+ mixins: [Mixin],
866
+
867
statics: {
900
- mixins: [Mixin],
868
abc: 'bar'
869
},
870
@@ -906,9 +873,10 @@ describe('ReactCompositeComponent', function() {
873
}
874
});
875
}).toThrow(
909
- 'Invariant Violation: ReactCompositeComponentInterface: You are ' +
910
- 'attempting to define `abc` on your component more than once. This ' +
911
- 'conflict may be due to a mixin.'
876
+ 'Invariant Violation: ReactCompositeComponent: You are attempting to ' +
877
+ 'define `abc` on your component more than once, but that is only ' +
878
+ 'supported for functions, which are chained together. This conflict ' +
879
+ 'may be due to a mixin.'
880
);
881
});
882
});