Skip to content

Commit d480782

Browse files
philipp-spiessgaearon
authored andcommitted
Don’t error when returning an empty Fragment (#12966)
* Don’t error when returning an empty Fragment When a fragment is reconciled, we directly move onto it’s children. Since an empty `<React.Fragment/>` will have children of `undefined`, this would always throw. To fix this, we bail out in those cases. * Test the update path as well * Reuse existing code path * An even more explicit solution that also fixes Flow
1 parent 4ac6f13 commit d480782

2 files changed

Lines changed: 29 additions & 4 deletions

File tree

packages/react-dom/src/__tests__/ReactDOMFiber-test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,31 @@ describe('ReactDOMFiber', () => {
171171
expect(firstNode.tagName).toBe('DIV');
172172
});
173173

174+
it('renders an empty fragment', () => {
175+
const Div = () => <div />;
176+
const EmptyFragment = () => <React.Fragment />;
177+
const NonEmptyFragment = () => (
178+
<React.Fragment>
179+
<Div />
180+
</React.Fragment>
181+
);
182+
183+
ReactDOM.render(<EmptyFragment />, container);
184+
expect(container.firstChild).toBe(null);
185+
186+
ReactDOM.render(<NonEmptyFragment />, container);
187+
expect(container.firstChild.tagName).toBe('DIV');
188+
189+
ReactDOM.render(<EmptyFragment />, container);
190+
expect(container.firstChild).toBe(null);
191+
192+
ReactDOM.render(<Div />, container);
193+
expect(container.firstChild.tagName).toBe('DIV');
194+
195+
ReactDOM.render(<EmptyFragment />, container);
196+
expect(container.firstChild).toBe(null);
197+
});
198+
174199
let svgEls, htmlEls, mathEls;
175200
const expectSVG = {ref: el => svgEls.push(el)};
176201
const expectHTML = {ref: el => htmlEls.push(el)};

packages/react-reconciler/src/ReactChildFiber.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,12 +1208,12 @@ function ChildReconciler(shouldTrackSideEffects) {
12081208
// Handle top level unkeyed fragments as if they were arrays.
12091209
// This leads to an ambiguity between <>{[...]}</> and <>...</>.
12101210
// We treat the ambiguous cases above the same.
1211-
if (
1211+
const isUnkeyedTopLevelFragment =
12121212
typeof newChild === 'object' &&
12131213
newChild !== null &&
12141214
newChild.type === REACT_FRAGMENT_TYPE &&
1215-
newChild.key === null
1216-
) {
1215+
newChild.key === null;
1216+
if (isUnkeyedTopLevelFragment) {
12171217
newChild = newChild.props.children;
12181218
}
12191219

@@ -1281,7 +1281,7 @@ function ChildReconciler(shouldTrackSideEffects) {
12811281
warnOnFunctionType();
12821282
}
12831283
}
1284-
if (typeof newChild === 'undefined') {
1284+
if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
12851285
// If the new child is undefined, and the return fiber is a composite
12861286
// component, throw an error. If Fiber return types are disabled,
12871287
// we already threw above.

0 commit comments

Comments
 (0)