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
Philipp Spieß committed
Jun 11, 2018 at 15:43 UTC
d480782c4162431d06c077ebf8fdf6b8ba7896ef
2 files changed
+29
-4
packages/react-dom/src/__tests__/ReactDOMFiber-test.js
+25
@@ -171,6 +171,31 @@ describe('ReactDOMFiber', () => {
171
expect(firstNode.tagName).toBe('DIV');
172
});
173
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
+
199
let svgEls, htmlEls, mathEls;
200
const expectSVG = {ref: el => svgEls.push(el)};
201
const expectHTML = {ref: el => htmlEls.push(el)};
packages/react-reconciler/src/ReactChildFiber.js
+4
-4
@@ -1208,12 +1208,12 @@ function ChildReconciler(shouldTrackSideEffects) {
1208
// Handle top level unkeyed fragments as if they were arrays.
1209
// This leads to an ambiguity between <>{[...]}</> and <>...</>.
1210
// We treat the ambiguous cases above the same.
1211
- if (
1211
+ const isUnkeyedTopLevelFragment =
1212
typeof newChild === 'object' &&
1213
newChild !== null &&
1214
newChild.type === REACT_FRAGMENT_TYPE &&
1215
- newChild.key === null
1216
- ) {
1215
+ newChild.key === null;
1216
+ if (isUnkeyedTopLevelFragment) {
1217
newChild = newChild.props.children;
1218
}
1219
@@ -1281,7 +1281,7 @@ function ChildReconciler(shouldTrackSideEffects) {
1281
warnOnFunctionType();
1282
}
1283
}
1284
- if (typeof newChild === 'undefined') {
1284
+ if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
1285
// If the new child is undefined, and the return fiber is a composite
1286
// component, throw an error. If Fiber return types are disabled,
1287
// we already threw above.