@samitouri / QOS-React-2 / commits / 757756f682

Enable null return values in plain functions

jim committed Jan 19, 2016 at 13:11 UTC 757756f682ab11961479e3c10adb0444271fdb9e
5 files changed +56 -91
src/isomorphic/modern/class/__tests__/ReactES6Class-test.js
+1 -3
@@ -64,9 +64,7 @@ describe('ReactES6Class', function() {
64 expect(console.error.calls.length).toBe(1);
65 expect(console.error.argsForCall[0][0]).toBe(
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.'
67 + 'instance: you may have forgotten to define `render`.'
68 );
69 });
70
src/isomorphic/modern/class/__tests__/ReactTypeScriptClass-test.ts
+1 -3
@@ -319,9 +319,7 @@ describe('ReactTypeScriptClass', function() {
319 expect((<any>console.error).argsForCall.length).toBe(1);
320 expect((<any>console.error).argsForCall[0][0]).toBe(
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.'
322 + 'component instance: you may have forgotten to define `render`.'
323 );
324 });
325
src/renderers/shared/reconciler/ReactCompositeComponent.js
+13 -25
@@ -43,7 +43,16 @@ function StatelessComponent(Component) {
43 }
44 StatelessComponent.prototype.render = function() {
45 var Component = ReactInstanceMap.get(this)._currentElement.type;
46 - return Component(this.props, this.context, this.updater);
46 + var element = Component(this.props, this.context, this.updater);
47 + if (__DEV__) {
48 + warning(
49 + element === null || element === false || ReactElement.isValidElement(element),
50 + '%s must be a class extending React.Component or be a stateless ' +
51 + 'function that returns a valid React element.',
52 + Component.displayName || Component.name || 'Component'
53 + );
54 + }
55 + return element;
56 };
57
58 /**
@@ -147,13 +156,7 @@ var ReactCompositeComponentMixin = {
156 var inst;
157 var renderedElement;
158
150 - // This is a way to detect if Component is a stateless arrow function
151 - // component, which is not newable. It might not be 100% reliable but is
152 - // something we can do until we start detecting that Component extends
153 - // React.Component. We already assume that typeof Component === 'function'.
154 - var canInstantiate = 'prototype' in Component;
155 -
156 - if (canInstantiate) {
159 + if (Component.prototype && Component.prototype.isReactComponent) {
160 if (__DEV__) {
161 ReactCurrentOwner.current = this;
162 try {
@@ -164,10 +167,7 @@ var ReactCompositeComponentMixin = {
167 } else {
168 inst = new Component(publicProps, publicContext, ReactUpdateQueue);
169 }
167 - }
168 -
169 - if (!canInstantiate || inst === null || inst === false || ReactElement.isValidElement(inst)) {
170 - renderedElement = inst;
170 + } else {
171 inst = new StatelessComponent(Component);
172 }
173
@@ -178,19 +178,7 @@ var ReactCompositeComponentMixin = {
178 warning(
179 false,
180 '%s(...): No `render` method found on the returned component ' +
181 - 'instance: you may have forgotten to define `render`, returned ' +
182 - 'null/false from a stateless component, or tried to render an ' +
183 - 'element whose type is a function that isn\'t a React component.',
184 - Component.displayName || Component.name || 'Component'
185 - );
186 - } else {
187 - // We support ES6 inheriting from React.Component, the module pattern,
188 - // and stateless components, but not ES6 classes that don't extend
189 - warning(
190 - (Component.prototype && Component.prototype.isReactComponent) ||
191 - !canInstantiate ||
192 - !(inst instanceof Component),
193 - '%s(...): React component classes must extend React.Component.',
181 + 'instance: you may have forgotten to define `render`.',
182 Component.displayName || Component.name || 'Component'
183 );
184 }
src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
-44
@@ -1103,19 +1103,6 @@ describe('ReactCompositeComponent', function() {
1103 expect(a).toBe(b);
1104 });
1105
1106 - it('should warn when using non-React functions in JSX', function() {
1107 - function NotAComponent() {
1108 - return [<div />, <div />];
1109 - }
1110 - expect(function() {
1111 - ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
1112 - }).toThrow(); // has no method 'render'
1113 - expect(console.error.calls.length).toBe(1);
1114 - expect(console.error.argsForCall[0][0]).toContain(
1115 - 'NotAComponent(...): No `render` method found'
1116 - );
1117 - });
1118 -
1106 it('context should be passed down from the parent', function() {
1107 var Parent = React.createClass({
1108 childContextTypes: {
@@ -1247,37 +1234,6 @@ describe('ReactCompositeComponent', function() {
1234 expect(console.error.calls.length).toBe(0);
1235 });
1236
1250 - it('should warn when a class does not extend React.Component', function() {
1251 -
1252 - var container = document.createElement('div');
1253 -
1254 - class Foo {
1255 - render() {
1256 - return <span />;
1257 - }
1258 - }
1259 -
1260 - function Bar() { }
1261 - Bar.prototype = Object.create(React.Component.prototype);
1262 - Bar.prototype.render = function() {
1263 - return <span />;
1264 - };
1265 -
1266 - expect(console.error.calls.length).toBe(0);
1267 -
1268 - ReactDOM.render(<Bar />, container);
1269 -
1270 - expect(console.error.calls.length).toBe(0);
1271 -
1272 - ReactDOM.render(<Foo />, container);
1273 -
1274 - expect(console.error.calls.length).toBe(1);
1275 - expect(console.error.argsForCall[0][0]).toContain(
1276 - 'React component classes must extend React.Component'
1277 - );
1278 -
1279 - });
1280 -
1237 it('should warn when mutated props are passed', function() {
1238
1239 var container = document.createElement('div');
src/renderers/shared/reconciler/__tests__/ReactStatelessComponent-test.js
+41 -16
@@ -98,19 +98,19 @@ describe('ReactStatelessComponent', function() {
98 expect(el.textContent).toBe('mest');
99 });
100
101 - it('should support module pattern components', function() {
102 - function Child({test}) {
103 - return {
104 - render() {
105 - return <div>{test}</div>;
106 - },
107 - };
101 + it('should warn when stateless component returns array', function() {
102 + spyOn(console, 'error');
103 + function NotAComponent() {
104 + return [<div />, <div />];
105 }
109 -
110 - var el = document.createElement('div');
111 - ReactDOM.render(<Child test="test" />, el);
112 -
113 - expect(el.textContent).toBe('test');
106 + expect(function() {
107 + ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
108 + }).toThrow();
109 + expect(console.error.calls.length).toBe(1);
110 + expect(console.error.argsForCall[0][0]).toContain(
111 + 'NotAComponent must be a class extending React.Component or be a stateless ' +
112 + 'function that returns a valid React element.'
113 + );
114 });
115
116 it('should throw on string refs in pure functions', function() {
@@ -204,10 +204,6 @@ describe('ReactStatelessComponent', function() {
204 });
205
206 it('should work with arrow functions', function() {
207 - // TODO: actually use arrow functions, probably need node v4 and maybe
208 - // a separate file that we blacklist from the arrow function transform.
209 - // We can't actually test this without native arrow functions since the
210 - // issues (non-newable) don't apply to any other functions.
207 var Child = function() {
208 return <div />;
209 };
@@ -217,4 +213,33 @@ describe('ReactStatelessComponent', function() {
213
214 expect(() => ReactTestUtils.renderIntoDocument(<Child />)).not.toThrow();
215 });
216 +
217 + it('should allow simple functions to return null', function() {
218 + var Child = function() {
219 + return null;
220 + };
221 + expect(() => ReactTestUtils.renderIntoDocument(<Child />)).not.toThrow();
222 + });
223 +
224 + it('should allow simple functions to return false', function() {
225 + function Child() {
226 + return false;
227 + };
228 + expect(() => ReactTestUtils.renderIntoDocument(<Child />)).not.toThrow();
229 + });
230 +
231 + it('should warn when using non-React functions in JSX', function() {
232 + spyOn(console, 'error');
233 + function NotAComponent() {
234 + return [<div />, <div />];
235 + }
236 + expect(function() {
237 + ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
238 + }).toThrow(); // has no method 'render'
239 + expect(console.error.calls.length).toBe(1);
240 + expect(console.error.argsForCall[0][0]).toContain(
241 + 'Warning: NotAComponent must be a class extending React.Component or be a stateless ' +
242 + 'function that returns a valid React element.'
243 + );
244 + });
245 });