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

Require instantiable components to extend React.Component

Ben Alpert committed Aug 19, 2015 at 15:34 UTC f3b7749c373093dcb7e576023c45fb628c695740
11 files changed +73 -55
src/addons/__tests__/ReactFragment-test.js
+1 -1
@@ -40,7 +40,7 @@ describe('ReactFragment', function() {
40 });
41
42 it('should throw if a plain object even if it is in an owner', function() {
43 - class Foo {
43 + class Foo extends React.Component {
44 render() {
45 var children = {
46 a: <span />,
src/isomorphic/classic/class/ReactClass.js
+1
@@ -847,6 +847,7 @@ var ReactClass = {
847 };
848 Constructor.prototype = new ReactClassComponent();
849 Constructor.prototype.constructor = Constructor;
850 + Constructor.isReactClass = {};
851
852 injectedMixins.forEach(
853 mixSpecIntoComponent.bind(null, Constructor)
src/isomorphic/modern/class/ReactComponent.js
+2
@@ -29,6 +29,8 @@ function ReactComponent(props, context, updater) {
29 this.updater = updater || ReactNoopUpdateQueue;
30 }
31
32 +ReactComponent.isReactClass = {};
33 +
34 /**
35 * Sets a subset of the state. Always use this to mutate
36 * state. You should treat `this.state` as immutable.
src/isomorphic/modern/class/__tests__/ReactCoffeeScriptClass-test.coffee
+8 -8
@@ -55,7 +55,7 @@ describe 'ReactCoffeeScriptClass', ->
55 expect(console.error.calls[0].args[0]).toContain('No `render` method found on the returned component instance')
56
57 it 'renders a simple stateless component with prop', ->
58 - class Foo
58 + class Foo extends React.Component
59 render: ->
60 Inner
61 name: @props.bar
@@ -144,7 +144,7 @@ describe 'ReactCoffeeScriptClass', ->
144
145 it 'should throw with non-object in the initial state property', ->
146 [['an array'], 'a string', 1234].forEach (state) ->
147 - class Foo
147 + class Foo extends React.Component
148 constructor: ->
149 @state = state
150
@@ -220,7 +220,7 @@ describe 'ReactCoffeeScriptClass', ->
220
221 it 'will call all the normal life cycle methods', ->
222 lifeCycles = []
223 - class Foo
223 + class Foo extends React.Component
224 constructor: ->
225 @state = {}
226
@@ -308,7 +308,7 @@ describe 'ReactCoffeeScriptClass', ->
308
309 it 'should warn when misspelling shouldComponentUpdate', ->
310 spyOn console, 'error'
311 - class NamedComponent
311 + class NamedComponent extends React.Component
312 componentShouldUpdate: ->
313 false
314
@@ -326,7 +326,7 @@ describe 'ReactCoffeeScriptClass', ->
326
327 it 'should warn when misspelling componentWillReceiveProps', ->
328 spyOn console, 'error'
329 - class NamedComponent
329 + class NamedComponent extends React.Component
330 componentWillRecieveProps: ->
331 false
332
@@ -368,13 +368,13 @@ describe 'ReactCoffeeScriptClass', ->
368 )
369
370 it 'supports this.context passed via getChildContext', ->
371 - class Bar
371 + class Bar extends React.Component
372 @contextTypes:
373 bar: React.PropTypes.string
374 render: ->
375 div className: @context.bar
376
377 - class Foo
377 + class Foo extends React.Component
378 @childContextTypes:
379 bar: React.PropTypes.string
380 getChildContext: ->
@@ -385,7 +385,7 @@ describe 'ReactCoffeeScriptClass', ->
385 test React.createElement(Foo), 'DIV', 'bar-through-context'
386
387 it 'supports classic refs', ->
388 - class Foo
388 + class Foo extends React.Component
389 render: ->
390 Inner
391 name: 'foo'
src/isomorphic/modern/class/__tests__/ReactES6Class-test.js
+14 -13
@@ -63,16 +63,15 @@ describe('ReactES6Class', function() {
63
64 expect(console.error.calls.length).toBe(1);
65 expect(console.error.calls[0].args[0]).toBe(
66 - 'Warning: Foo(...): ' +
67 - 'No `render` method found on the returned component instance: you may ' +
68 - 'have forgotten to define `render` in your component or you may have ' +
69 - 'accidentally tried to render an element whose type is a function that ' +
70 - 'isn\'t a React component.'
66 + 'Warning: Foo(...): No `render` method found on the returned component ' +
67 + 'instance: you may have forgotten to define `render`, returned ' +
68 + 'null/false from a stateless component, or tried to render an element ' +
69 + 'whose type is a function that isn\'t a React component.'
70 );
71 });
72
73 it('renders a simple stateless component with prop', function() {
75 - class Foo {
74 + class Foo extends React.Component {
75 render() {
76 return <Inner name={this.props.bar} />;
77 }
@@ -167,8 +166,9 @@ describe('ReactES6Class', function() {
166
167 it('should throw with non-object in the initial state property', function() {
168 [['an array'], 'a string', 1234].forEach(function(state) {
170 - class Foo {
169 + class Foo extends React.Component {
170 constructor() {
171 + super();
172 this.state = state;
173 }
174 render() {
@@ -266,8 +266,9 @@ describe('ReactES6Class', function() {
266
267 it('will call all the normal life cycle methods', function() {
268 var lifeCycles = [];
269 - class Foo {
269 + class Foo extends React.Component {
270 constructor() {
271 + super();
272 this.state = {};
273 }
274 componentWillMount() {
@@ -360,7 +361,7 @@ describe('ReactES6Class', function() {
361 it('should warn when misspelling shouldComponentUpdate', function() {
362 spyOn(console, 'error');
363
363 - class NamedComponent {
364 + class NamedComponent extends React.Component {
365 componentShouldUpdate() {
366 return false;
367 }
@@ -382,7 +383,7 @@ describe('ReactES6Class', function() {
383 it('should warn when misspelling componentWillReceiveProps', function() {
384 spyOn(console, 'error');
385
385 - class NamedComponent {
386 + class NamedComponent extends React.Component {
387 componentWillRecieveProps() {
388 return false;
389 }
@@ -428,13 +429,13 @@ describe('ReactES6Class', function() {
429 });
430
431 it('supports this.context passed via getChildContext', function() {
431 - class Bar {
432 + class Bar extends React.Component {
433 render() {
434 return <div className={this.context.bar} />;
435 }
436 }
437 Bar.contextTypes = {bar: React.PropTypes.string};
437 - class Foo {
438 + class Foo extends React.Component {
439 getChildContext() {
440 return {bar: 'bar-through-context'};
441 }
@@ -447,7 +448,7 @@ describe('ReactES6Class', function() {
448 });
449
450 it('supports classic refs', function() {
450 - class Foo {
451 + class Foo extends React.Component {
452 render() {
453 return <Inner name="foo" ref="inner" />;
454 }
src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts
+13 -14
@@ -45,7 +45,7 @@ function test(element, expectedTag, expectedClassName) {
45 class Empty extends React.Component { }
46
47 // it renders a simple stateless component with prop
48 -class SimpleStateless {
48 +class SimpleStateless extends React.Component {
49 props : any;
50 render() {
51 return React.createElement(Inner, {name: this.props.bar});
@@ -124,19 +124,19 @@ class RenderOnce extends React.Component {
124 }
125
126 // it should throw with non-object in the initial state property
127 -class ArrayState {
127 +class ArrayState extends React.Component {
128 state = ['an array'];
129 render() {
130 return React.createElement('span');
131 }
132 }
133 -class StringState {
133 +class StringState extends React.Component {
134 state = 'a string';
135 render() {
136 return React.createElement('span');
137 }
138 }
139 -class NumberState {
139 +class NumberState extends React.Component {
140 state = 1234;
141 render() {
142 return React.createElement('span');
@@ -203,7 +203,7 @@ class ForceUpdateWithNoState extends React.Component {
203
204 // it will call all the normal life cycle methods
205 var lifeCycles = [];
206 -class NormalLifeCycles {
206 +class NormalLifeCycles extends React.Component {
207 props : any;
208 state = {};
209 componentWillMount() {
@@ -254,7 +254,7 @@ class ClassicProperties extends React.Component {
254 }
255
256 // it should warn when misspelling shouldComponentUpdate
257 -class MisspelledComponent1 {
257 +class MisspelledComponent1 extends React.Component {
258 componentShouldUpdate() {
259 return false;
260 }
@@ -264,7 +264,7 @@ class MisspelledComponent1 {
264 }
265
266 // it should warn when misspelling componentWillReceiveProps
267 -class MisspelledComponent2 {
267 +class MisspelledComponent2 extends React.Component {
268 componentWillRecieveProps() {
269 return false;
270 }
@@ -280,7 +280,7 @@ class ReadContext extends React.Component {
280 return React.createElement('div', { className: this.context.bar });
281 }
282 }
283 -class ProvideContext {
283 +class ProvideContext extends React.Component {
284 static childContextTypes = { bar: React.PropTypes.string };
285 getChildContext() {
286 return { bar: 'bar-through-context' };
@@ -291,7 +291,7 @@ class ProvideContext {
291 }
292
293 // it supports classic refs
294 -class ClassicRefs {
294 +class ClassicRefs extends React.Component {
295 render() {
296 return React.createElement(Inner, {name: 'foo', ref: 'inner'});
297 }
@@ -318,11 +318,10 @@ describe('ReactTypeScriptClass', function() {
318
319 expect((<any>console.error).argsForCall.length).toBe(1);
320 expect((<any>console.error).argsForCall[0][0]).toBe(
321 - 'Warning: Empty(...): ' +
322 - 'No `render` method found on the returned component instance: you may ' +
323 - 'have forgotten to define `render` in your component or you may have ' +
324 - 'accidentally tried to render an element whose type is a function that ' +
325 - 'isn\'t a React component.'
321 + 'Warning: Empty(...): No `render` method found on the returned ' +
322 + 'component instance: you may have forgotten to define `render`, ' +
323 + 'returned null/false from a stateless component, or tried to render an ' +
324 + 'element whose type is a function that isn\'t a React component.'
325 );
326 });
327
src/isomorphic/modern/element/__tests__/ReactJSXElement-test.js
+2 -2
@@ -24,7 +24,7 @@ describe('ReactJSXElement', function() {
24 React = require('React');
25 ReactDOM = require('ReactDOM');
26 ReactTestUtils = require('ReactTestUtils');
27 - Component = class {
27 + Component = class extends React.Component {
28 render() {
29 return <div />;
30 }
@@ -176,7 +176,7 @@ describe('ReactJSXElement', function() {
176 });
177
178 it('should normalize props with default values', function() {
179 - class NormalizingComponent {
179 + class NormalizingComponent extends React.Component {
180 render() {
181 return <span>{this.props.prop}</span>;
182 }
src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js
+9 -9
@@ -27,13 +27,13 @@ describe('ReactJSXElementValidator', function() {
27 React = require('React');
28 ReactTestUtils = require('ReactTestUtils');
29
30 - Component = class {
30 + Component = class extends React.Component {
31 render() {
32 return <div />;
33 }
34 };
35
36 - RequiredPropComponent = class {
36 + RequiredPropComponent = class extends React.Component {
37 render() {
38 return <span>{this.props.prop}</span>;
39 }
@@ -56,13 +56,13 @@ describe('ReactJSXElementValidator', function() {
56 it('warns for keys for arrays of elements with owner info', function() {
57 spyOn(console, 'error');
58
59 - class InnerComponent {
59 + class InnerComponent extends React.Component {
60 render() {
61 return <Component>{this.props.childSet}</Component>;
62 }
63 }
64
65 - class ComponentWrapper {
65 + class ComponentWrapper extends React.Component {
66 render() {
67 return (
68 <InnerComponent
@@ -181,7 +181,7 @@ describe('ReactJSXElementValidator', function() {
181 // component, we give a small hint as to which parent instantiated that
182 // component as per warnings about key usage in ReactElementValidator.
183 spyOn(console, 'error');
184 - class MyComp {
184 + class MyComp extends React.Component {
185 render() {
186 return <div>My color is {this.color}</div>;
187 }
@@ -189,7 +189,7 @@ describe('ReactJSXElementValidator', function() {
189 MyComp.propTypes = {
190 color: React.PropTypes.string,
191 };
192 - class ParentComp {
192 + class ParentComp extends React.Component {
193 render() {
194 return <MyComp color={123} />;
195 }
@@ -290,7 +290,7 @@ describe('ReactJSXElementValidator', function() {
290 // actually occurs. Since this step is skipped in production, we should just
291 // warn instead of throwing for this case.
292 spyOn(console, 'error');
293 - class NullPropTypeComponent {
293 + class NullPropTypeComponent extends React.Component {
294 render() {
295 return <span>{this.props.prop}</span>;
296 }
@@ -308,7 +308,7 @@ describe('ReactJSXElementValidator', function() {
308
309 it('should warn on invalid context types', function() {
310 spyOn(console, 'error');
311 - class NullContextTypeComponent {
311 + class NullContextTypeComponent extends React.Component {
312 render() {
313 return <span>{this.props.prop}</span>;
314 }
@@ -326,7 +326,7 @@ describe('ReactJSXElementValidator', function() {
326
327 it('should warn if getDefaultProps is specificed on the class', function() {
328 spyOn(console, 'error');
329 - class GetDefaultPropsComponent {
329 + class GetDefaultPropsComponent extends React.Component {
330 render() {
331 return <span>{this.props.prop}</span>;
332 }
src/renderers/dom/client/ReactMount.js
+4
@@ -373,6 +373,10 @@ function hasNonRootReactChild(node) {
373 * here.
374 */
375 var TopLevelWrapper = function() {};
376 +TopLevelWrapper.isReactClass = {};
377 +if (__DEV__) {
378 + TopLevelWrapper.displayName = 'TopLevelWrapper';
379 +}
380 TopLevelWrapper.prototype.render = function() {
381 // this.props is actually a ReactElement
382 return this.props;
src/renderers/shared/reconciler/ReactCompositeComponent.js
+18 -8
@@ -155,14 +155,24 @@ var ReactCompositeComponentMixin = {
155 if (__DEV__) {
156 // This will throw later in _renderValidatedComponent, but add an early
157 // warning now to help debugging
158 - warning(
159 - inst.render != null,
160 - '%s(...): No `render` method found on the returned component ' +
161 - 'instance: you may have forgotten to define `render` in your ' +
162 - 'component or you may have accidentally tried to render an element ' +
163 - 'whose type is a function that isn\'t a React component.',
164 - Component.displayName || Component.name || 'Component'
165 - );
158 + if (inst.render == null) {
159 + warning(
160 + false,
161 + '%s(...): No `render` method found on the returned component ' +
162 + 'instance: you may have forgotten to define `render`, returned ' +
163 + 'null/false from a stateless component, or tried to render an ' +
164 + 'element whose type is a function that isn\'t a React component.',
165 + Component.displayName || Component.name || 'Component'
166 + );
167 + } else {
168 + // We support ES6 inheriting from React.Component, the module pattern,
169 + // and stateless components, but not ES6 classes that don't extend
170 + warning(
171 + Component.isReactClass || !(inst instanceof Component),
172 + '%s(...): React component classes must extend React.Component.',
173 + Component.displayName || Component.name || 'Component'
174 + );
175 + }
176 }
177
178 // These should be set up in the constructor, but as a convenience for
src/renderers/shared/reconciler/ReactEmptyComponent.js
+1
@@ -28,6 +28,7 @@ var ReactEmptyComponentInjection = {
28 };
29
30 var ReactEmptyComponentType = function() {};
31 +ReactEmptyComponentType.isReactClass = {};
32 ReactEmptyComponentType.prototype.componentDidMount = function() {
33 var internalInstance = ReactInstanceMap.get(this);
34 // TODO: Make sure we run these methods in the correct order, we shouldn't