@samitouri / QOS-React / commits / 14bac6193a

Allow components to render undefined (#21869)

Ricky committed Jul 13, 2021 at 15:48 UTC 14bac6193a334eda42e727336e8967419f08f5df
10 files changed +286 -416
packages/react-dom/src/__tests__/ReactDOMServerIntegrationElements-test.js
+28 -24
@@ -924,33 +924,37 @@ describe('ReactDOMServerIntegration', () => {
924 );
925 });
926
927 - describe('components that throw errors', function() {
928 - itThrowsWhenRendering(
929 - 'a function returning undefined',
930 - async render => {
931 - const UndefinedComponent = () => undefined;
932 - await render(<UndefinedComponent />, 1);
933 - },
934 - 'UndefinedComponent(...): Nothing was returned from render. ' +
935 - 'This usually means a return statement is missing. Or, to ' +
936 - 'render nothing, return null.',
937 - );
927 + describe('components that render nullish', function() {
928 + itRenders('a function returning null', async render => {
929 + const NullComponent = () => null;
930 + await render(<NullComponent />);
931 + });
932
939 - itThrowsWhenRendering(
940 - 'a class returning undefined',
941 - async render => {
942 - class UndefinedComponent extends React.Component {
943 - render() {
944 - return undefined;
945 - }
933 + itRenders('a class returning null', async render => {
934 + class NullComponent extends React.Component {
935 + render() {
936 + return null;
937 }
947 - await render(<UndefinedComponent />, 1);
948 - },
949 - 'UndefinedComponent(...): Nothing was returned from render. ' +
950 - 'This usually means a return statement is missing. Or, to ' +
951 - 'render nothing, return null.',
952 - );
938 + }
939 + await render(<NullComponent />);
940 + });
941 +
942 + itRenders('a function returning undefined', async render => {
943 + const UndefinedComponent = () => undefined;
944 + await render(<UndefinedComponent />);
945 + });
946 +
947 + itRenders('a class returning undefined', async render => {
948 + class UndefinedComponent extends React.Component {
949 + render() {
950 + return undefined;
951 + }
952 + }
953 + await render(<UndefinedComponent />);
954 + });
955 + });
956
957 + describe('components that throw errors', function() {
958 itThrowsWhenRendering(
959 'a function returning an object',
960 async render => {
packages/react-dom/src/__tests__/ReactEmptyComponent-test.js
+246 -228
@@ -45,145 +45,117 @@ describe('ReactEmptyComponent', () => {
45 };
46 });
47
48 - it('should not produce child DOM nodes for null and false', () => {
49 - class Component1 extends React.Component {
50 - render() {
51 - return null;
52 - }
53 - }
54 -
55 - class Component2 extends React.Component {
56 - render() {
57 - return false;
48 + describe.each([null, undefined])('when %s', nullORUndefined => {
49 + it('should not throw when rendering', () => {
50 + class Component extends React.Component {
51 + render() {
52 + return nullORUndefined;
53 + }
54 }
59 - }
60 -
61 - const container1 = document.createElement('div');
62 - ReactDOM.render(<Component1 />, container1);
63 - expect(container1.children.length).toBe(0);
64 -
65 - const container2 = document.createElement('div');
66 - ReactDOM.render(<Component2 />, container2);
67 - expect(container2.children.length).toBe(0);
68 - });
69 -
70 - it('should still throw when rendering to undefined', () => {
71 - class Component extends React.Component {
72 - render() {}
73 - }
74 -
75 - expect(function() {
76 - ReactTestUtils.renderIntoDocument(<Component />);
77 - }).toThrowError(
78 - 'Component(...): Nothing was returned from render. This usually means a return statement is missing. ' +
79 - 'Or, to render nothing, return null.',
80 - );
81 - });
55
83 - it('should be able to switch between rendering null and a normal tag', () => {
84 - const instance1 = (
85 - <TogglingComponent firstComponent={null} secondComponent={'div'} />
86 - );
87 - const instance2 = (
88 - <TogglingComponent firstComponent={'div'} secondComponent={null} />
89 - );
56 + expect(function() {
57 + ReactTestUtils.renderIntoDocument(<Component />);
58 + }).not.toThrowError();
59 + });
60
91 - ReactTestUtils.renderIntoDocument(instance1);
92 - ReactTestUtils.renderIntoDocument(instance2);
61 + it('should not produce child DOM nodes for nullish and false', () => {
62 + class Component1 extends React.Component {
63 + render() {
64 + return nullORUndefined;
65 + }
66 + }
67
94 - expect(log).toHaveBeenCalledTimes(4);
95 - expect(log).toHaveBeenNthCalledWith(1, null);
96 - expect(log).toHaveBeenNthCalledWith(
97 - 2,
98 - expect.objectContaining({tagName: 'DIV'}),
99 - );
100 - expect(log).toHaveBeenNthCalledWith(
101 - 3,
102 - expect.objectContaining({tagName: 'DIV'}),
103 - );
104 - expect(log).toHaveBeenNthCalledWith(4, null);
105 - });
68 + class Component2 extends React.Component {
69 + render() {
70 + return false;
71 + }
72 + }
73
107 - it('should be able to switch in a list of children', () => {
108 - const instance1 = (
109 - <TogglingComponent firstComponent={null} secondComponent={'div'} />
110 - );
74 + const container1 = document.createElement('div');
75 + ReactDOM.render(<Component1 />, container1);
76 + expect(container1.children.length).toBe(0);
77
112 - ReactTestUtils.renderIntoDocument(
113 - <div>
114 - {instance1}
115 - {instance1}
116 - {instance1}
117 - </div>,
118 - );
78 + const container2 = document.createElement('div');
79 + ReactDOM.render(<Component2 />, container2);
80 + expect(container2.children.length).toBe(0);
81 + });
82
120 - expect(log).toHaveBeenCalledTimes(6);
121 - expect(log).toHaveBeenNthCalledWith(1, null);
122 - expect(log).toHaveBeenNthCalledWith(2, null);
123 - expect(log).toHaveBeenNthCalledWith(3, null);
124 - expect(log).toHaveBeenNthCalledWith(
125 - 4,
126 - expect.objectContaining({tagName: 'DIV'}),
127 - );
128 - expect(log).toHaveBeenNthCalledWith(
129 - 5,
130 - expect.objectContaining({tagName: 'DIV'}),
131 - );
132 - expect(log).toHaveBeenNthCalledWith(
133 - 6,
134 - expect.objectContaining({tagName: 'DIV'}),
135 - );
136 - });
137 -
138 - it('should distinguish between a script placeholder and an actual script tag', () => {
139 - const instance1 = (
140 - <TogglingComponent firstComponent={null} secondComponent={'script'} />
141 - );
142 - const instance2 = (
143 - <TogglingComponent firstComponent={'script'} secondComponent={null} />
144 - );
83 + it('should be able to switch between rendering nullish and a normal tag', () => {
84 + const instance1 = (
85 + <TogglingComponent
86 + firstComponent={nullORUndefined}
87 + secondComponent={'div'}
88 + />
89 + );
90 + const instance2 = (
91 + <TogglingComponent
92 + firstComponent={'div'}
93 + secondComponent={nullORUndefined}
94 + />
95 + );
96
146 - expect(function() {
97 ReactTestUtils.renderIntoDocument(instance1);
148 - }).not.toThrow();
149 - expect(function() {
98 ReactTestUtils.renderIntoDocument(instance2);
151 - }).not.toThrow();
99
153 - expect(log).toHaveBeenCalledTimes(4);
154 - expect(log).toHaveBeenNthCalledWith(1, null);
155 - expect(log).toHaveBeenNthCalledWith(
156 - 2,
157 - expect.objectContaining({tagName: 'SCRIPT'}),
158 - );
159 - expect(log).toHaveBeenNthCalledWith(
160 - 3,
161 - expect.objectContaining({tagName: 'SCRIPT'}),
162 - );
163 - expect(log).toHaveBeenNthCalledWith(4, null);
164 - });
100 + expect(log).toHaveBeenCalledTimes(4);
101 + expect(log).toHaveBeenNthCalledWith(1, null);
102 + expect(log).toHaveBeenNthCalledWith(
103 + 2,
104 + expect.objectContaining({tagName: 'DIV'}),
105 + );
106 + expect(log).toHaveBeenNthCalledWith(
107 + 3,
108 + expect.objectContaining({tagName: 'DIV'}),
109 + );
110 + expect(log).toHaveBeenNthCalledWith(4, null);
111 + });
112
166 - it(
167 - 'should have findDOMNode return null when multiple layers of composite ' +
168 - 'components render to the same null placeholder',
169 - () => {
170 - class GrandChild extends React.Component {
171 - render() {
172 - return null;
173 - }
174 - }
113 + it('should be able to switch in a list of children', () => {
114 + const instance1 = (
115 + <TogglingComponent
116 + firstComponent={nullORUndefined}
117 + secondComponent={'div'}
118 + />
119 + );
120
176 - class Child extends React.Component {
177 - render() {
178 - return <GrandChild />;
179 - }
180 - }
121 + ReactTestUtils.renderIntoDocument(
122 + <div>
123 + {instance1}
124 + {instance1}
125 + {instance1}
126 + </div>,
127 + );
128 +
129 + expect(log).toHaveBeenCalledTimes(6);
130 + expect(log).toHaveBeenNthCalledWith(1, null);
131 + expect(log).toHaveBeenNthCalledWith(2, null);
132 + expect(log).toHaveBeenNthCalledWith(3, null);
133 + expect(log).toHaveBeenNthCalledWith(
134 + 4,
135 + expect.objectContaining({tagName: 'DIV'}),
136 + );
137 + expect(log).toHaveBeenNthCalledWith(
138 + 5,
139 + expect.objectContaining({tagName: 'DIV'}),
140 + );
141 + expect(log).toHaveBeenNthCalledWith(
142 + 6,
143 + expect.objectContaining({tagName: 'DIV'}),
144 + );
145 + });
146
147 + it('should distinguish between a script placeholder and an actual script tag', () => {
148 const instance1 = (
183 - <TogglingComponent firstComponent={'div'} secondComponent={Child} />
149 + <TogglingComponent
150 + firstComponent={nullORUndefined}
151 + secondComponent={'script'}
152 + />
153 );
154 const instance2 = (
186 - <TogglingComponent firstComponent={Child} secondComponent={'div'} />
155 + <TogglingComponent
156 + firstComponent={'script'}
157 + secondComponent={nullORUndefined}
158 + />
159 );
160
161 expect(function() {
@@ -194,146 +166,192 @@ describe('ReactEmptyComponent', () => {
166 }).not.toThrow();
167
168 expect(log).toHaveBeenCalledTimes(4);
169 + expect(log).toHaveBeenNthCalledWith(1, null);
170 expect(log).toHaveBeenNthCalledWith(
198 - 1,
199 - expect.objectContaining({tagName: 'DIV'}),
171 + 2,
172 + expect.objectContaining({tagName: 'SCRIPT'}),
173 );
201 - expect(log).toHaveBeenNthCalledWith(2, null);
202 - expect(log).toHaveBeenNthCalledWith(3, null);
174 expect(log).toHaveBeenNthCalledWith(
204 - 4,
205 - expect.objectContaining({tagName: 'DIV'}),
175 + 3,
176 + expect.objectContaining({tagName: 'SCRIPT'}),
177 );
207 - },
208 - );
178 + expect(log).toHaveBeenNthCalledWith(4, null);
179 + });
180 +
181 + it(
182 + 'should have findDOMNode return null when multiple layers of composite ' +
183 + 'components render to the same nullish placeholder',
184 + () => {
185 + class GrandChild extends React.Component {
186 + render() {
187 + return nullORUndefined;
188 + }
189 + }
190
210 - it('works when switching components', () => {
211 - let assertions = 0;
191 + class Child extends React.Component {
192 + render() {
193 + return <GrandChild />;
194 + }
195 + }
196
213 - class Inner extends React.Component {
214 - render() {
215 - return <span />;
216 - }
197 + const instance1 = (
198 + <TogglingComponent firstComponent={'div'} secondComponent={Child} />
199 + );
200 + const instance2 = (
201 + <TogglingComponent firstComponent={Child} secondComponent={'div'} />
202 + );
203
218 - componentDidMount() {
219 - // Make sure the DOM node resolves properly even if we're replacing a
220 - // `null` component
221 - expect(ReactDOM.findDOMNode(this)).not.toBe(null);
222 - assertions++;
223 - }
204 + expect(function() {
205 + ReactTestUtils.renderIntoDocument(instance1);
206 + }).not.toThrow();
207 + expect(function() {
208 + ReactTestUtils.renderIntoDocument(instance2);
209 + }).not.toThrow();
210 +
211 + expect(log).toHaveBeenCalledTimes(4);
212 + expect(log).toHaveBeenNthCalledWith(
213 + 1,
214 + expect.objectContaining({tagName: 'DIV'}),
215 + );
216 + expect(log).toHaveBeenNthCalledWith(2, null);
217 + expect(log).toHaveBeenNthCalledWith(3, null);
218 + expect(log).toHaveBeenNthCalledWith(
219 + 4,
220 + expect.objectContaining({tagName: 'DIV'}),
221 + );
222 + },
223 + );
224
225 - componentWillUnmount() {
226 - // Even though we're getting replaced by `null`, we haven't been
227 - // replaced yet!
228 - expect(ReactDOM.findDOMNode(this)).not.toBe(null);
229 - assertions++;
225 + it('works when switching components', () => {
226 + let assertions = 0;
227 +
228 + class Inner extends React.Component {
229 + render() {
230 + return <span />;
231 + }
232 +
233 + componentDidMount() {
234 + // Make sure the DOM node resolves properly even if we're replacing a
235 + // `null` component
236 + expect(ReactDOM.findDOMNode(this)).not.toBe(null);
237 + assertions++;
238 + }
239 +
240 + componentWillUnmount() {
241 + // Even though we're getting replaced by `null`, we haven't been
242 + // replaced yet!
243 + expect(ReactDOM.findDOMNode(this)).not.toBe(null);
244 + assertions++;
245 + }
246 }
231 - }
247
233 - class Wrapper extends React.Component {
234 - render() {
235 - return this.props.showInner ? <Inner /> : null;
248 + class Wrapper extends React.Component {
249 + render() {
250 + return this.props.showInner ? <Inner /> : nullORUndefined;
251 + }
252 }
237 - }
253
239 - const el = document.createElement('div');
240 - let component;
254 + const el = document.createElement('div');
255 + let component;
256
242 - // Render the <Inner /> component...
243 - component = ReactDOM.render(<Wrapper showInner={true} />, el);
244 - expect(ReactDOM.findDOMNode(component)).not.toBe(null);
257 + // Render the <Inner /> component...
258 + component = ReactDOM.render(<Wrapper showInner={true} />, el);
259 + expect(ReactDOM.findDOMNode(component)).not.toBe(null);
260
246 - // Switch to null...
247 - component = ReactDOM.render(<Wrapper showInner={false} />, el);
248 - expect(ReactDOM.findDOMNode(component)).toBe(null);
261 + // Switch to null...
262 + component = ReactDOM.render(<Wrapper showInner={false} />, el);
263 + expect(ReactDOM.findDOMNode(component)).toBe(null);
264
250 - // ...then switch back.
251 - component = ReactDOM.render(<Wrapper showInner={true} />, el);
252 - expect(ReactDOM.findDOMNode(component)).not.toBe(null);
265 + // ...then switch back.
266 + component = ReactDOM.render(<Wrapper showInner={true} />, el);
267 + expect(ReactDOM.findDOMNode(component)).not.toBe(null);
268
254 - expect(assertions).toBe(3);
255 - });
269 + expect(assertions).toBe(3);
270 + });
271
257 - it('can render null at the top level', () => {
258 - const div = document.createElement('div');
259 - ReactDOM.render(null, div);
260 - expect(div.innerHTML).toBe('');
261 - });
272 + it('can render nullish at the top level', () => {
273 + const div = document.createElement('div');
274 + ReactDOM.render(nullORUndefined, div);
275 + expect(div.innerHTML).toBe('');
276 + });
277
263 - it('does not break when updating during mount', () => {
264 - class Child extends React.Component {
265 - componentDidMount() {
266 - if (this.props.onMount) {
267 - this.props.onMount();
278 + it('does not break when updating during mount', () => {
279 + class Child extends React.Component {
280 + componentDidMount() {
281 + if (this.props.onMount) {
282 + this.props.onMount();
283 + }
284 }
269 - }
285
271 - render() {
272 - if (!this.props.visible) {
273 - return null;
274 - }
286 + render() {
287 + if (!this.props.visible) {
288 + return nullORUndefined;
289 + }
290
276 - return <div>hello world</div>;
291 + return <div>hello world</div>;
292 + }
293 }
278 - }
294
280 - class Parent extends React.Component {
281 - update = () => {
282 - this.forceUpdate();
283 - };
295 + class Parent extends React.Component {
296 + update = () => {
297 + this.forceUpdate();
298 + };
299
285 - render() {
286 - return (
287 - <div>
288 - <Child key="1" visible={false} />
289 - <Child key="0" visible={true} onMount={this.update} />
290 - <Child key="2" visible={false} />
291 - </div>
292 - );
300 + render() {
301 + return (
302 + <div>
303 + <Child key="1" visible={false} />
304 + <Child key="0" visible={true} onMount={this.update} />
305 + <Child key="2" visible={false} />
306 + </div>
307 + );
308 + }
309 }
294 - }
310
296 - expect(function() {
297 - ReactTestUtils.renderIntoDocument(<Parent />);
298 - }).not.toThrow();
299 - });
311 + expect(function() {
312 + ReactTestUtils.renderIntoDocument(<Parent />);
313 + }).not.toThrow();
314 + });
315
301 - it('preserves the dom node during updates', () => {
302 - class Empty extends React.Component {
303 - render() {
304 - return null;
316 + it('preserves the dom node during updates', () => {
317 + class Empty extends React.Component {
318 + render() {
319 + return nullORUndefined;
320 + }
321 }
306 - }
322
308 - const container = document.createElement('div');
323 + const container = document.createElement('div');
324
310 - ReactDOM.render(<Empty />, container);
311 - const noscript1 = container.firstChild;
312 - expect(noscript1).toBe(null);
325 + ReactDOM.render(<Empty />, container);
326 + const noscript1 = container.firstChild;
327 + expect(noscript1).toBe(null);
328
314 - // This update shouldn't create a DOM node
315 - ReactDOM.render(<Empty />, container);
316 - const noscript2 = container.firstChild;
317 - expect(noscript2).toBe(null);
318 - });
329 + // This update shouldn't create a DOM node
330 + ReactDOM.render(<Empty />, container);
331 + const noscript2 = container.firstChild;
332 + expect(noscript2).toBe(null);
333 + });
334
320 - it('should warn about React.forwardRef that returns undefined', () => {
321 - const Empty = () => {};
322 - const EmptyForwardRef = React.forwardRef(Empty);
335 + it('should not warn about React.forwardRef that returns nullish', () => {
336 + const Empty = () => {
337 + return nullORUndefined;
338 + };
339 + const EmptyForwardRef = React.forwardRef(Empty);
340
324 - expect(() => {
325 - ReactTestUtils.renderIntoDocument(<EmptyForwardRef />);
326 - }).toThrowError(
327 - 'ForwardRef(Empty)(...): Nothing was returned from render.',
328 - );
329 - });
341 + expect(() => {
342 + ReactTestUtils.renderIntoDocument(<EmptyForwardRef />);
343 + }).not.toThrowError();
344 + });
345
331 - it('should warn about React.memo that returns undefined', () => {
332 - const Empty = () => {};
333 - const EmptyMemo = React.memo(Empty);
346 + it('should not warn about React.memo that returns nullish', () => {
347 + const Empty = () => {
348 + return nullORUndefined;
349 + };
350 + const EmptyMemo = React.memo(Empty);
351
335 - expect(() => {
336 - ReactTestUtils.renderIntoDocument(<EmptyMemo />);
337 - }).toThrowError('Empty(...): Nothing was returned from render.');
352 + expect(() => {
353 + ReactTestUtils.renderIntoDocument(<EmptyMemo />);
354 + }).not.toThrowError();
355 + });
356 });
357 });
packages/react-dom/src/__tests__/ReactFunctionComponent-test.js
+2 -5
@@ -136,7 +136,7 @@ describe('ReactFunctionComponent', () => {
136 );
137 });
138
139 - it('should throw when stateless component returns undefined', () => {
139 + it('should not throw when stateless component returns undefined', () => {
140 function NotAComponent() {}
141 expect(function() {
142 ReactTestUtils.renderIntoDocument(
@@ -144,10 +144,7 @@ describe('ReactFunctionComponent', () => {
144 <NotAComponent />
145 </div>,
146 );
147 - }).toThrowError(
148 - 'NotAComponent(...): Nothing was returned from render. ' +
149 - 'This usually means a return statement is missing. Or, to render nothing, return null.',
150 - );
147 + }).not.toThrowError();
148 });
149
150 it('should throw on string refs in pure functions', () => {
packages/react-dom/src/__tests__/ReactMockedComponent-test.js
+5 -29
@@ -30,42 +30,18 @@ describe('ReactMockedComponent', () => {
30 MockedComponent.prototype.render = jest.fn();
31 });
32
33 - it('should allow a mocked component to be rendered in dev', () => {
33 + it('should allow a mocked component to be rendered', () => {
34 const container = document.createElement('container');
35 - if (__DEV__) {
36 - ReactDOM.render(<MockedComponent />, container);
37 - } else {
38 - expect(() => ReactDOM.render(<MockedComponent />, container)).toThrow(
39 - 'Nothing was returned from render.',
40 - );
41 - }
35 + ReactDOM.render(<MockedComponent />, container);
36 });
37
38 it('should allow a mocked component to be updated in dev', () => {
39 const container = document.createElement('container');
46 - if (__DEV__) {
47 - ReactDOM.render(<MockedComponent />, container);
48 - } else {
49 - expect(() => ReactDOM.render(<MockedComponent />, container)).toThrow(
50 - 'Nothing was returned from render.',
51 - );
52 - }
53 - if (__DEV__) {
54 - ReactDOM.render(<MockedComponent />, container);
55 - } else {
56 - expect(() => ReactDOM.render(<MockedComponent />, container)).toThrow(
57 - 'Nothing was returned from render.',
58 - );
59 - }
40 + ReactDOM.render(<MockedComponent />, container);
41 + ReactDOM.render(<MockedComponent />, container);
42 });
43
44 it('should allow a mocked component to be rendered in dev (SSR)', () => {
63 - if (__DEV__) {
64 - ReactDOMServer.renderToString(<MockedComponent />);
65 - } else {
66 - expect(() => ReactDOMServer.renderToString(<MockedComponent />)).toThrow(
67 - 'Nothing was returned from render.',
68 - );
69 - }
45 + ReactDOMServer.renderToString(<MockedComponent />);
46 });
47 });
packages/react-dom/src/__tests__/utils/ReactDOMServerIntegrationTestUtils.js
+1 -1
@@ -242,7 +242,7 @@ module.exports = function(initModules) {
242 await asyncReactDOMRender(element, cleanContainer, true);
243 // This gives us the expected text content.
244 const cleanTextContent =
245 - cleanContainer.lastChild && cleanContainer.lastChild.textContent;
245 + (cleanContainer.lastChild && cleanContainer.lastChild.textContent) || '';
246
247 // The only guarantee is that text content has been patched up if needed.
248 expect(hydratedTextContent).toBe(cleanTextContent);
packages/react-dom/src/server/ReactPartialRenderer.js
-22
@@ -391,18 +391,6 @@ function createOpenTagMarkup(
391 return ret;
392 }
393
394 -function validateRenderResult(child, type) {
395 - if (child === undefined) {
396 - invariant(
397 - false,
398 - '%s(...): Nothing was returned from render. This usually means a ' +
399 - 'return statement is missing. Or, to render nothing, ' +
400 - 'return null.',
401 - getComponentNameFromType(type) || 'Component',
402 - );
403 - }
404 -}
405 -
394 function resolve(
395 child: mixed,
396 context: Object,
@@ -631,7 +619,6 @@ function resolve(
619 inst.render == null
620 ) {
621 child = inst;
634 - validateRenderResult(child, Component);
622 return;
623 }
624 }
@@ -720,15 +707,6 @@ function resolve(
707 }
708 child = inst.render();
709
723 - if (__DEV__) {
724 - if (child === undefined && inst.render._isMockFunction) {
725 - // This is probably bad practice. Consider warning here and
726 - // deprecating this convenience.
727 - child = null;
728 - }
729 - }
730 - validateRenderResult(child, Component);
731 -
710 let childContext;
711 if (disableLegacyContext) {
712 if (__DEV__) {
packages/react-reconciler/src/ReactChildFiber.new.js
+1 -39
@@ -21,15 +21,7 @@ import {
21 REACT_PORTAL_TYPE,
22 REACT_LAZY_TYPE,
23 } from 'shared/ReactSymbols';
24 -import {
25 - FunctionComponent,
26 - ClassComponent,
27 - HostText,
28 - HostPortal,
29 - ForwardRef,
30 - Fragment,
31 - SimpleMemoComponent,
32 -} from './ReactWorkTags';
24 +import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
25 import invariant from 'shared/invariant';
26 import isArray from 'shared/isArray';
27 import {
@@ -1302,36 +1294,6 @@ function ChildReconciler(shouldTrackSideEffects) {
1294 warnOnFunctionType(returnFiber);
1295 }
1296 }
1305 - if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
1306 - // If the new child is undefined, and the return fiber is a composite
1307 - // component, throw an error. If Fiber return types are disabled,
1308 - // we already threw above.
1309 - switch (returnFiber.tag) {
1310 - case ClassComponent: {
1311 - if (__DEV__) {
1312 - const instance = returnFiber.stateNode;
1313 - if (instance.render._isMockFunction) {
1314 - // We allow auto-mocks to proceed as if they're returning null.
1315 - break;
1316 - }
1317 - }
1318 - }
1319 - // Intentionally fall through to the next case, which handles both
1320 - // functions and classes
1321 - // eslint-disable-next-lined no-fallthrough
1322 - case FunctionComponent:
1323 - case ForwardRef:
1324 - case SimpleMemoComponent: {
1325 - invariant(
1326 - false,
1327 - '%s(...): Nothing was returned from render. This usually means a ' +
1328 - 'return statement is missing. Or, to render nothing, ' +
1329 - 'return null.',
1330 - getComponentNameFromFiber(returnFiber) || 'Component',
1331 - );
1332 - }
1333 - }
1334 - }
1297
1298 // Remaining cases are all treated as empty.
1299 return deleteRemainingChildren(returnFiber, currentFirstChild);
packages/react-reconciler/src/ReactChildFiber.old.js
+1 -39
@@ -21,15 +21,7 @@ import {
21 REACT_PORTAL_TYPE,
22 REACT_LAZY_TYPE,
23 } from 'shared/ReactSymbols';
24 -import {
25 - FunctionComponent,
26 - ClassComponent,
27 - HostText,
28 - HostPortal,
29 - ForwardRef,
30 - Fragment,
31 - SimpleMemoComponent,
32 -} from './ReactWorkTags';
24 +import {ClassComponent, HostText, HostPortal, Fragment} from './ReactWorkTags';
25 import invariant from 'shared/invariant';
26 import isArray from 'shared/isArray';
27 import {
@@ -1302,36 +1294,6 @@ function ChildReconciler(shouldTrackSideEffects) {
1294 warnOnFunctionType(returnFiber);
1295 }
1296 }
1305 - if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
1306 - // If the new child is undefined, and the return fiber is a composite
1307 - // component, throw an error. If Fiber return types are disabled,
1308 - // we already threw above.
1309 - switch (returnFiber.tag) {
1310 - case ClassComponent: {
1311 - if (__DEV__) {
1312 - const instance = returnFiber.stateNode;
1313 - if (instance.render._isMockFunction) {
1314 - // We allow auto-mocks to proceed as if they're returning null.
1315 - break;
1316 - }
1317 - }
1318 - }
1319 - // Intentionally fall through to the next case, which handles both
1320 - // functions and classes
1321 - // eslint-disable-next-lined no-fallthrough
1322 - case FunctionComponent:
1323 - case ForwardRef:
1324 - case SimpleMemoComponent: {
1325 - invariant(
1326 - false,
1327 - '%s(...): Nothing was returned from render. This usually means a ' +
1328 - 'return statement is missing. Or, to render nothing, ' +
1329 - 'return null.',
1330 - getComponentNameFromFiber(returnFiber) || 'Component',
1331 - );
1332 - }
1333 - }
1334 - }
1297
1298 // Remaining cases are all treated as empty.
1299 return deleteRemainingChildren(returnFiber, currentFirstChild);
packages/react-server/src/ReactFizzServer.js
+1 -22
@@ -554,16 +554,6 @@ function shouldConstruct(Component) {
554 return Component.prototype && Component.prototype.isReactComponent;
555 }
556
557 -function invalidRenderResult(type: any): void {
558 - invariant(
559 - false,
560 - '%s(...): Nothing was returned from render. This usually means a ' +
561 - 'return statement is missing. Or, to render nothing, ' +
562 - 'return null.',
563 - getComponentNameFromType(type) || 'Component',
564 - );
565 -}
566 -
557 function renderWithHooks<Props, SecondArg>(
558 request: Request,
559 task: Task,
@@ -574,11 +564,7 @@ function renderWithHooks<Props, SecondArg>(
564 const componentIdentity = {};
565 prepareToUseHooks(componentIdentity);
566 const result = Component(props, secondArg);
577 - const children = finishHooks(Component, props, result, secondArg);
578 - if (children === undefined) {
579 - invalidRenderResult(Component);
580 - }
581 - return children;
567 + return finishHooks(Component, props, result, secondArg);
568 }
569
570 function finishClassComponent(
@@ -589,13 +575,6 @@ function finishClassComponent(
575 props: any,
576 ): ReactNodeList {
577 const nextChildren = instance.render();
592 - if (nextChildren === undefined) {
593 - if (__DEV__ && instance.render._isMockFunction) {
594 - // We allow auto-mocks to proceed as if they're returning null.
595 - } else {
596 - invalidRenderResult(Component);
597 - }
598 - }
578
579 if (__DEV__) {
580 if (instance.props !== props) {
packages/shared/__tests__/ReactError-test.internal.js
+1 -7
@@ -46,18 +46,12 @@ describe('ReactError', () => {
46 );
47 });
48
49 - // @gate build === "production"
49 it('should serialize arguments', () => {
50 function Oops() {
51 return;
52 }
53 Oops.displayName = '#wtf';
54 const container = document.createElement('div');
56 - expect(() => ReactDOM.render(<Oops />, container)).toThrowError(
57 - 'Minified React error #152; visit ' +
58 - 'https://reactjs.org/docs/error-decoder.html?invariant=152&args[]=%23wtf' +
59 - ' for the full message or use the non-minified dev environment' +
60 - ' for full errors and additional helpful warnings.',
61 - );
55 + expect(() => ReactDOM.render(<Oops />, container)).not.toThrowError();
56 });
57 });