@samitouri / QOS-React-2 / commits / d17bd176a2

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 });