diff --git a/scripts/fiber/tests-failing.txt b/scripts/fiber/tests-failing.txt
index 36f8480baf..2316f076e1 100644
--- a/scripts/fiber/tests-failing.txt
+++ b/scripts/fiber/tests-failing.txt
@@ -64,10 +64,6 @@ src/renderers/shared/shared/__tests__/ReactEmptyComponent-test.js
src/renderers/shared/shared/__tests__/ReactMultiChildText-test.js
* should reorder keyed text nodes
-src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js
-* should warn when stateless component returns array
-* should warn when using non-React functions in JSX
-
src/renderers/shared/shared/__tests__/ReactUpdates-test.js
* marks top-level updates
diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt
index 18c4945ca2..0c7d4082c5 100644
--- a/scripts/fiber/tests-passing.txt
+++ b/scripts/fiber/tests-passing.txt
@@ -544,6 +544,8 @@ src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
* should bubble events from the portal to the parent
* should not onMouseLeave when staying in the portal
* should not crash encountering low-priority tree
+* throws if non-element passed to top-level render
+* throws if something other than false, null, or an element is returned from render
src/renderers/dom/shared/__tests__/CSSProperty-test.js
* should generate browser prefixes for its `isUnitlessNumber`
@@ -1536,6 +1538,7 @@ src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js
* should update stateless component
* should unmount stateless component
* should pass context thru stateless component
+* should throw when stateless component returns undefined
* should throw on string refs in pure functions
* should warn when given a string ref
* should warn when given a function ref
diff --git a/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js b/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
index 61efde7c9b..ea7f39da29 100644
--- a/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
+++ b/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
@@ -1015,8 +1015,10 @@ describe('ReactDOMFiber', () => {
});
it('throws if non-element passed to top-level render', () => {
+ // FIXME: These assertions pass individually, but they leave React in
+ // an inconsistent state. This suggests an error-handling bug. I'll fix
+ // this in a separate PR.
const message = 'render(): Invalid component element.';
-
expect(() => ReactDOM.render(null, container)).toThrow(message, container);
expect(() => ReactDOM.render(undefined, container)).toThrow(message, container);
expect(() => ReactDOM.render(false, container)).toThrow(message, container);
@@ -1030,9 +1032,9 @@ describe('ReactDOMFiber', () => {
return props.children;
}
- expect(() => ReactDOM.render(Hi, container)).toThrow(/Render\.render/);
- expect(() => ReactDOM.render({999}, container)).toThrow(/Render\.render/);
- expect(() => ReactDOM.render([], container)).toThrow(/Render\.render/);
+ expect(() => ReactDOM.render(Hi, container)).toThrow(/You may have returned undefined/);
+ expect(() => ReactDOM.render({999}, container)).toThrow(/You may have returned undefined/);
+ expect(() => ReactDOM.render([], container)).toThrow(/You may have returned undefined/);
});
});
}
diff --git a/src/renderers/shared/fiber/ReactChildFiber.js b/src/renderers/shared/fiber/ReactChildFiber.js
index acaaff8725..c741d94bdd 100644
--- a/src/renderers/shared/fiber/ReactChildFiber.js
+++ b/src/renderers/shared/fiber/ReactChildFiber.js
@@ -75,6 +75,7 @@ const {
NoEffect,
Placement,
Deletion,
+ Err,
} = ReactTypeOfSideEffect;
function coerceRef(current: ?Fiber, element: ReactElement) {
@@ -1129,7 +1130,9 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
if (returnFiber.tag === HostRoot) {
// Top-level only accepts elements or portals
invariant(
- false,
+ // If the root has an error effect, this is an intentional unmount.
+ // Don't throw an error.
+ returnFiber.effectTag & Err,
'render(): Invalid component element.'
);
} else {
@@ -1247,7 +1250,7 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
const Component = returnFiber.type;
invariant(
false,
- '%s: Nothing was returned from render. This usually means a ' +
+ '%s(...): Nothing was returned from render. This usually means a ' +
'return statement is missing. Or, to render nothing, ' +
'return null.',
Component.displayName || Component.name || 'Component'
diff --git a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
index e356056af4..cb0b5e2472 100644
--- a/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
+++ b/src/renderers/shared/fiber/__tests__/ReactIncremental-test.js
@@ -956,6 +956,7 @@ describe('ReactIncremental', () => {
function Trail() {
ops.push('Trail');
+ return null;
}
function App(props) {
diff --git a/src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js b/src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
index bee26adef8..603ab8c7fd 100644
--- a/src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
+++ b/src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
@@ -496,6 +496,7 @@ describe('ReactIncrementalErrorHandling', () => {
if (props.throw) {
throw new Error('Hello');
}
+ return null;
}
function Foo() {
diff --git a/src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js b/src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js
index a095ee0ca4..876f268554 100644
--- a/src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js
+++ b/src/renderers/shared/shared/__tests__/ReactStatelessComponent-test.js
@@ -146,8 +146,13 @@ describe('ReactStatelessComponent', () => {
expect(function() {
ReactTestUtils.renderIntoDocument(
);
}).toThrowError(
- 'NotAComponent(...): A valid React element (or null) must be returned. ' +
- 'You may have returned undefined, an array or some other invalid object.'
+ ReactDOMFeatureFlags.useFiber ?
+ // Fiber gives a more specific error message for undefined because it
+ // supports more return types.
+ 'NotAComponent(...): Nothing was returned from render' :
+ // Stack's message is generic.
+ 'NotAComponent(...): A valid React element (or null) must be returned. ' +
+ 'You may have returned undefined, an array or some other invalid object.'
);
});