Put React.Fragment under a feature flag (#11421)
* Put React.Fragment under a feature flag * Don't export undefined Fragment
Clement Hoang committed
Oct 31, 2017 at 17:43 UTC
0e15ff5669271f5f5956e30a4fe1ae5963778e89
7 files changed
+631
-600
packages/react-cs-renderer/src/ReactNativeCSFeatureFlags.js
+1
@@ -14,6 +14,7 @@ import type {FeatureFlags} from 'shared/ReactFeatureFlags';
14
var ReactNativeCSFeatureFlags: FeatureFlags = {
15
enableAsyncSubtreeAPI: true,
16
enableAsyncSchedulingByDefaultInReactDOM: false,
17
+ enableReactFragment: false,
18
// React Native CS uses persistent reconciler.
19
enableMutatingReconciler: false,
20
enableNoopReconciler: false,
packages/react-dom/src/__tests__/ReactDOMServerIntegration-test.js
+48
-43
@@ -17,6 +17,7 @@ let ReactDOMServer;
17
let ReactTestUtils;
18
19
const stream = require('stream');
20
+const ReactFeatureFlags = require('shared/ReactFeatureFlags');
21
22
const TEXT_NODE_TYPE = 3;
23
@@ -403,54 +404,56 @@ describe('ReactDOMServerIntegration', () => {
404
expect(parent.childNodes[2].tagName).toBe('P');
405
});
406
406
- itRenders('a fragment with one child', async render => {
407
- let e = await render(<React.Fragment><div>text1</div></React.Fragment>);
408
- let parent = e.parentNode;
409
- expect(parent.childNodes[0].tagName).toBe('DIV');
410
- });
411
-
412
- itRenders('a fragment with several children', async render => {
413
- let Header = props => {
414
- return <p>header</p>;
415
- };
416
- let Footer = props => {
417
- return <React.Fragment><h2>footer</h2><h3>about</h3></React.Fragment>;
418
- };
419
- let e = await render(
420
- <React.Fragment>
421
- <div>text1</div>
422
- <span>text2</span>
423
- <Header />
424
- <Footer />
425
- </React.Fragment>,
426
- );
427
- let parent = e.parentNode;
428
- expect(parent.childNodes[0].tagName).toBe('DIV');
429
- expect(parent.childNodes[1].tagName).toBe('SPAN');
430
- expect(parent.childNodes[2].tagName).toBe('P');
431
- expect(parent.childNodes[3].tagName).toBe('H2');
432
- expect(parent.childNodes[4].tagName).toBe('H3');
433
- });
407
+ if (ReactFeatureFlags.enableReactFragment) {
408
+ itRenders('a fragment with one child', async render => {
409
+ let e = await render(<React.Fragment><div>text1</div></React.Fragment>);
410
+ let parent = e.parentNode;
411
+ expect(parent.childNodes[0].tagName).toBe('DIV');
412
+ });
413
435
- itRenders('a nested fragment', async render => {
436
- let e = await render(
437
- <React.Fragment>
414
+ itRenders('a fragment with several children', async render => {
415
+ let Header = props => {
416
+ return <p>header</p>;
417
+ };
418
+ let Footer = props => {
419
+ return <React.Fragment><h2>footer</h2><h3>about</h3></React.Fragment>;
420
+ };
421
+ let e = await render(
422
<React.Fragment>
423
<div>text1</div>
440
- </React.Fragment>
441
- <span>text2</span>
424
+ <span>text2</span>
425
+ <Header />
426
+ <Footer />
427
+ </React.Fragment>,
428
+ );
429
+ let parent = e.parentNode;
430
+ expect(parent.childNodes[0].tagName).toBe('DIV');
431
+ expect(parent.childNodes[1].tagName).toBe('SPAN');
432
+ expect(parent.childNodes[2].tagName).toBe('P');
433
+ expect(parent.childNodes[3].tagName).toBe('H2');
434
+ expect(parent.childNodes[4].tagName).toBe('H3');
435
+ });
436
+
437
+ itRenders('a nested fragment', async render => {
438
+ let e = await render(
439
<React.Fragment>
440
<React.Fragment>
444
- <React.Fragment>{null}<p /></React.Fragment>{false}
441
+ <div>text1</div>
442
</React.Fragment>
446
- </React.Fragment>
447
- </React.Fragment>,
448
- );
449
- let parent = e.parentNode;
450
- expect(parent.childNodes[0].tagName).toBe('DIV');
451
- expect(parent.childNodes[1].tagName).toBe('SPAN');
452
- expect(parent.childNodes[2].tagName).toBe('P');
453
- });
443
+ <span>text2</span>
444
+ <React.Fragment>
445
+ <React.Fragment>
446
+ <React.Fragment>{null}<p /></React.Fragment>{false}
447
+ </React.Fragment>
448
+ </React.Fragment>
449
+ </React.Fragment>,
450
+ );
451
+ let parent = e.parentNode;
452
+ expect(parent.childNodes[0].tagName).toBe('DIV');
453
+ expect(parent.childNodes[1].tagName).toBe('SPAN');
454
+ expect(parent.childNodes[2].tagName).toBe('P');
455
+ });
456
+ }
457
458
itRenders('an iterable', async render => {
459
const threeDivIterable = {
@@ -484,7 +487,9 @@ describe('ReactDOMServerIntegration', () => {
487
// but server returns empty HTML. So we compare parent text.
488
expect((await render(<div>{''}</div>)).textContent).toBe('');
489
487
- expect(await render(<React.Fragment />)).toBe(null);
490
+ if (ReactFeatureFlags.enableReactFragment) {
491
+ expect(await render(<React.Fragment />)).toBe(null);
492
+ }
493
expect(await render([])).toBe(null);
494
expect(await render(false)).toBe(null);
495
expect(await render(true)).toBe(null);
packages/react-reconciler/src/ReactChildFiber.js
+2
@@ -18,6 +18,7 @@ import type {
18
19
var ReactTypeOfSideEffect = require('shared/ReactTypeOfSideEffect');
20
var ReactTypeOfWork = require('shared/ReactTypeOfWork');
21
+var ReactFeatureFlags = require('shared/ReactFeatureFlags');
22
var emptyObject = require('fbjs/lib/emptyObject');
23
var invariant = require('fbjs/lib/invariant');
24
var {REACT_PORTAL_TYPE} = require('./ReactPortal');
@@ -1432,6 +1433,7 @@ function ChildReconciler(shouldClone, shouldTrackSideEffects) {
1433
// This leads to an ambiguity between <>{[...]}</> and <>...</>.
1434
// We treat the ambiguous cases above the same.
1435
if (
1436
+ ReactFeatureFlags.enableReactFragment &&
1437
typeof newChild === 'object' &&
1438
newChild !== null &&
1439
newChild.type === REACT_FRAGMENT_TYPE &&
packages/react-reconciler/src/__tests__/ReactFragment-test.js
+508
-498
@@ -8,6 +8,8 @@
8
*/
9
'use strict';
10
11
+var ReactFeatureFlags = require('shared/ReactFeatureFlags');
12
+
13
let React;
14
let ReactNoop;
15
@@ -31,691 +33,699 @@ describe('ReactFragment', () => {
33
return {type: 'div', children, prop: undefined};
34
}
35
34
- it('should render a single child via noop renderer', () => {
35
- const element = (
36
- <React.Fragment>
37
- <span>foo</span>
38
- </React.Fragment>
39
- );
36
+ if (ReactFeatureFlags.enableReactFragment) {
37
+ it('should render a single child via noop renderer', () => {
38
+ const element = (
39
+ <React.Fragment>
40
+ <span>foo</span>
41
+ </React.Fragment>
42
+ );
43
41
- ReactNoop.render(element);
42
- ReactNoop.flush();
44
+ ReactNoop.render(element);
45
+ ReactNoop.flush();
46
44
- expect(ReactNoop.getChildren()).toEqual([span()]);
45
- });
47
+ expect(ReactNoop.getChildren()).toEqual([span()]);
48
+ });
49
47
- it('should render zero children via noop renderer', () => {
48
- const element = <React.Fragment />;
50
+ it('should render zero children via noop renderer', () => {
51
+ const element = <React.Fragment />;
52
50
- ReactNoop.render(element);
51
- ReactNoop.flush();
53
+ ReactNoop.render(element);
54
+ ReactNoop.flush();
55
53
- expect(ReactNoop.getChildren()).toEqual([]);
54
- });
56
+ expect(ReactNoop.getChildren()).toEqual([]);
57
+ });
58
56
- it('should render multiple children via noop renderer', () => {
57
- const element = (
58
- <React.Fragment>
59
- hello <span>world</span>
60
- </React.Fragment>
61
- );
59
+ it('should render multiple children via noop renderer', () => {
60
+ const element = (
61
+ <React.Fragment>
62
+ hello <span>world</span>
63
+ </React.Fragment>
64
+ );
65
63
- ReactNoop.render(element);
64
- ReactNoop.flush();
66
+ ReactNoop.render(element);
67
+ ReactNoop.flush();
68
66
- expect(ReactNoop.getChildren()).toEqual([text('hello '), span()]);
67
- });
69
+ expect(ReactNoop.getChildren()).toEqual([text('hello '), span()]);
70
+ });
71
69
- it('should render an iterable via noop renderer', () => {
70
- const element = (
71
- <React.Fragment>
72
- {new Set([<span key="a">hi</span>, <span key="b">bye</span>])}
73
- </React.Fragment>
74
- );
72
+ it('should render an iterable via noop renderer', () => {
73
+ const element = (
74
+ <React.Fragment>
75
+ {new Set([<span key="a">hi</span>, <span key="b">bye</span>])}
76
+ </React.Fragment>
77
+ );
78
76
- ReactNoop.render(element);
77
- ReactNoop.flush();
79
+ ReactNoop.render(element);
80
+ ReactNoop.flush();
81
79
- expect(ReactNoop.getChildren()).toEqual([span(), span()]);
80
- });
82
+ expect(ReactNoop.getChildren()).toEqual([span(), span()]);
83
+ });
84
82
- it('should preserve state of children with 1 level nesting', function() {
83
- var ops = [];
85
+ it('should preserve state of children with 1 level nesting', function() {
86
+ var ops = [];
87
85
- class Stateful extends React.Component {
86
- componentDidUpdate() {
87
- ops.push('Update Stateful');
88
+ class Stateful extends React.Component {
89
+ componentDidUpdate() {
90
+ ops.push('Update Stateful');
91
+ }
92
+
93
+ render() {
94
+ return <div>Hello</div>;
95
+ }
96
}
97
90
- render() {
91
- return <div>Hello</div>;
98
+ function Foo({condition}) {
99
+ return condition
100
+ ? <Stateful key="a" />
101
+ : <React.Fragment>
102
+ <Stateful key="a" />
103
+ <div key="b">World</div>
104
+ </React.Fragment>;
105
}
93
- }
106
95
- function Foo({condition}) {
96
- return condition
97
- ? <Stateful key="a" />
98
- : <React.Fragment>
99
- <Stateful key="a" />
100
- <div key="b">World</div>
101
- </React.Fragment>;
102
- }
107
+ ReactNoop.render(<Foo condition={true} />);
108
+ ReactNoop.flush();
109
104
- ReactNoop.render(<Foo condition={true} />);
105
- ReactNoop.flush();
110
+ ReactNoop.render(<Foo condition={false} />);
111
+ ReactNoop.flush();
112
107
- ReactNoop.render(<Foo condition={false} />);
108
- ReactNoop.flush();
113
+ expect(ops).toEqual(['Update Stateful']);
114
+ expect(ReactNoop.getChildren()).toEqual([div(), div()]);
115
110
- expect(ops).toEqual(['Update Stateful']);
111
- expect(ReactNoop.getChildren()).toEqual([div(), div()]);
116
+ ReactNoop.render(<Foo condition={true} />);
117
+ ReactNoop.flush();
118
113
- ReactNoop.render(<Foo condition={true} />);
114
- ReactNoop.flush();
119
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
120
+ expect(ReactNoop.getChildren()).toEqual([div()]);
121
+ });
122
116
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
117
- expect(ReactNoop.getChildren()).toEqual([div()]);
118
- });
123
+ it('should preserve state between top-level fragments', function() {
124
+ var ops = [];
125
120
- it('should preserve state between top-level fragments', function() {
121
- var ops = [];
126
+ class Stateful extends React.Component {
127
+ componentDidUpdate() {
128
+ ops.push('Update Stateful');
129
+ }
130
123
- class Stateful extends React.Component {
124
- componentDidUpdate() {
125
- ops.push('Update Stateful');
131
+ render() {
132
+ return <div>Hello</div>;
133
+ }
134
}
135
128
- render() {
129
- return <div>Hello</div>;
136
+ function Foo({condition}) {
137
+ return condition
138
+ ? <React.Fragment>
139
+ <Stateful />
140
+ </React.Fragment>
141
+ : <React.Fragment>
142
+ <Stateful />
143
+ </React.Fragment>;
144
}
131
- }
145
133
- function Foo({condition}) {
134
- return condition
135
- ? <React.Fragment>
136
- <Stateful />
137
- </React.Fragment>
138
- : <React.Fragment>
139
- <Stateful />
140
- </React.Fragment>;
141
- }
146
+ ReactNoop.render(<Foo condition={true} />);
147
+ ReactNoop.flush();
148
143
- ReactNoop.render(<Foo condition={true} />);
144
- ReactNoop.flush();
149
+ ReactNoop.render(<Foo condition={false} />);
150
+ ReactNoop.flush();
151
146
- ReactNoop.render(<Foo condition={false} />);
147
- ReactNoop.flush();
152
+ expect(ops).toEqual(['Update Stateful']);
153
+ expect(ReactNoop.getChildren()).toEqual([div()]);
154
149
- expect(ops).toEqual(['Update Stateful']);
150
- expect(ReactNoop.getChildren()).toEqual([div()]);
155
+ ReactNoop.render(<Foo condition={true} />);
156
+ ReactNoop.flush();
157
152
- ReactNoop.render(<Foo condition={true} />);
153
- ReactNoop.flush();
158
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
159
+ expect(ReactNoop.getChildren()).toEqual([div()]);
160
+ });
161
155
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
156
- expect(ReactNoop.getChildren()).toEqual([div()]);
157
- });
158
-
159
- it('should preserve state of children nested at same level', function() {
160
- var ops = [];
162
+ it('should preserve state of children nested at same level', function() {
163
+ var ops = [];
164
162
- class Stateful extends React.Component {
163
- componentDidUpdate() {
164
- ops.push('Update Stateful');
165
- }
165
+ class Stateful extends React.Component {
166
+ componentDidUpdate() {
167
+ ops.push('Update Stateful');
168
+ }
169
167
- render() {
168
- return <div>Hello</div>;
170
+ render() {
171
+ return <div>Hello</div>;
172
+ }
173
}
170
- }
174
172
- function Foo({condition}) {
173
- return condition
174
- ? <React.Fragment>
175
- <React.Fragment>
175
+ function Foo({condition}) {
176
+ return condition
177
+ ? <React.Fragment>
178
<React.Fragment>
177
- <Stateful key="a" />
179
+ <React.Fragment>
180
+ <Stateful key="a" />
181
+ </React.Fragment>
182
</React.Fragment>
183
</React.Fragment>
180
- </React.Fragment>
181
- : <React.Fragment>
182
- <React.Fragment>
184
+ : <React.Fragment>
185
<React.Fragment>
184
- <div />
185
- <Stateful key="a" />
186
+ <React.Fragment>
187
+ <div />
188
+ <Stateful key="a" />
189
+ </React.Fragment>
190
</React.Fragment>
187
- </React.Fragment>
188
- </React.Fragment>;
189
- }
191
+ </React.Fragment>;
192
+ }
193
191
- ReactNoop.render(<Foo condition={true} />);
192
- ReactNoop.flush();
194
+ ReactNoop.render(<Foo condition={true} />);
195
+ ReactNoop.flush();
196
194
- ReactNoop.render(<Foo condition={false} />);
195
- ReactNoop.flush();
197
+ ReactNoop.render(<Foo condition={false} />);
198
+ ReactNoop.flush();
199
197
- expect(ops).toEqual(['Update Stateful']);
198
- expect(ReactNoop.getChildren()).toEqual([div(), div()]);
200
+ expect(ops).toEqual(['Update Stateful']);
201
+ expect(ReactNoop.getChildren()).toEqual([div(), div()]);
202
200
- ReactNoop.render(<Foo condition={true} />);
201
- ReactNoop.flush();
203
+ ReactNoop.render(<Foo condition={true} />);
204
+ ReactNoop.flush();
205
203
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
204
- expect(ReactNoop.getChildren()).toEqual([div()]);
205
- });
206
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
207
+ expect(ReactNoop.getChildren()).toEqual([div()]);
208
+ });
209
207
- it('should not preserve state in non-top-level fragment nesting', function() {
208
- var ops = [];
210
+ it('should not preserve state in non-top-level fragment nesting', function() {
211
+ var ops = [];
212
210
- class Stateful extends React.Component {
211
- componentDidUpdate() {
212
- ops.push('Update Stateful');
213
+ class Stateful extends React.Component {
214
+ componentDidUpdate() {
215
+ ops.push('Update Stateful');
216
+ }
217
+
218
+ render() {
219
+ return <div>Hello</div>;
220
+ }
221
}
222
215
- render() {
216
- return <div>Hello</div>;
223
+ function Foo({condition}) {
224
+ return condition
225
+ ? <React.Fragment>
226
+ <React.Fragment><Stateful key="a" /></React.Fragment>
227
+ </React.Fragment>
228
+ : <React.Fragment><Stateful key="a" /></React.Fragment>;
229
}
218
- }
230
220
- function Foo({condition}) {
221
- return condition
222
- ? <React.Fragment>
223
- <React.Fragment><Stateful key="a" /></React.Fragment>
224
- </React.Fragment>
225
- : <React.Fragment><Stateful key="a" /></React.Fragment>;
226
- }
231
+ ReactNoop.render(<Foo condition={true} />);
232
+ ReactNoop.flush();
233
228
- ReactNoop.render(<Foo condition={true} />);
229
- ReactNoop.flush();
234
+ ReactNoop.render(<Foo condition={false} />);
235
+ ReactNoop.flush();
236
231
- ReactNoop.render(<Foo condition={false} />);
232
- ReactNoop.flush();
237
+ expect(ops).toEqual([]);
238
+ expect(ReactNoop.getChildren()).toEqual([div()]);
239
234
- expect(ops).toEqual([]);
235
- expect(ReactNoop.getChildren()).toEqual([div()]);
240
+ ReactNoop.render(<Foo condition={true} />);
241
+ ReactNoop.flush();
242
237
- ReactNoop.render(<Foo condition={true} />);
238
- ReactNoop.flush();
243
+ expect(ops).toEqual([]);
244
+ expect(ReactNoop.getChildren()).toEqual([div()]);
245
+ });
246
240
- expect(ops).toEqual([]);
241
- expect(ReactNoop.getChildren()).toEqual([div()]);
242
- });
247
+ it('should not preserve state of children if nested 2 levels without siblings', function() {
248
+ var ops = [];
249
244
- it('should not preserve state of children if nested 2 levels without siblings', function() {
245
- var ops = [];
250
+ class Stateful extends React.Component {
251
+ componentDidUpdate() {
252
+ ops.push('Update Stateful');
253
+ }
254
247
- class Stateful extends React.Component {
248
- componentDidUpdate() {
249
- ops.push('Update Stateful');
255
+ render() {
256
+ return <div>Hello</div>;
257
+ }
258
}
259
252
- render() {
253
- return <div>Hello</div>;
260
+ function Foo({condition}) {
261
+ return condition
262
+ ? <Stateful key="a" />
263
+ : <React.Fragment>
264
+ <React.Fragment>
265
+ <Stateful key="a" />
266
+ </React.Fragment>
267
+ </React.Fragment>;
268
}
255
- }
269
257
- function Foo({condition}) {
258
- return condition
259
- ? <Stateful key="a" />
260
- : <React.Fragment>
261
- <React.Fragment>
262
- <Stateful key="a" />
263
- </React.Fragment>
264
- </React.Fragment>;
265
- }
270
+ ReactNoop.render(<Foo condition={true} />);
271
+ ReactNoop.flush();
272
267
- ReactNoop.render(<Foo condition={true} />);
268
- ReactNoop.flush();
273
+ ReactNoop.render(<Foo condition={false} />);
274
+ ReactNoop.flush();
275
270
- ReactNoop.render(<Foo condition={false} />);
271
- ReactNoop.flush();
276
+ expect(ops).toEqual([]);
277
+ expect(ReactNoop.getChildren()).toEqual([div()]);
278
273
- expect(ops).toEqual([]);
274
- expect(ReactNoop.getChildren()).toEqual([div()]);
279
+ ReactNoop.render(<Foo condition={true} />);
280
+ ReactNoop.flush();
281
276
- ReactNoop.render(<Foo condition={true} />);
277
- ReactNoop.flush();
282
+ expect(ops).toEqual([]);
283
+ expect(ReactNoop.getChildren()).toEqual([div()]);
284
+ });
285
279
- expect(ops).toEqual([]);
280
- expect(ReactNoop.getChildren()).toEqual([div()]);
281
- });
286
+ it('should not preserve state of children if nested 2 levels with siblings', function() {
287
+ var ops = [];
288
283
- it('should not preserve state of children if nested 2 levels with siblings', function() {
284
- var ops = [];
289
+ class Stateful extends React.Component {
290
+ componentDidUpdate() {
291
+ ops.push('Update Stateful');
292
+ }
293
286
- class Stateful extends React.Component {
287
- componentDidUpdate() {
288
- ops.push('Update Stateful');
294
+ render() {
295
+ return <div>Hello</div>;
296
+ }
297
}
298
291
- render() {
292
- return <div>Hello</div>;
299
+ function Foo({condition}) {
300
+ return condition
301
+ ? <Stateful key="a" />
302
+ : <React.Fragment>
303
+ <React.Fragment>
304
+ <Stateful key="a" />
305
+ </React.Fragment>
306
+ <div />
307
+ </React.Fragment>;
308
}
294
- }
309
296
- function Foo({condition}) {
297
- return condition
298
- ? <Stateful key="a" />
299
- : <React.Fragment>
300
- <React.Fragment>
301
- <Stateful key="a" />
302
- </React.Fragment>
303
- <div />
304
- </React.Fragment>;
305
- }
310
+ ReactNoop.render(<Foo condition={true} />);
311
+ ReactNoop.flush();
312
307
- ReactNoop.render(<Foo condition={true} />);
308
- ReactNoop.flush();
313
+ ReactNoop.render(<Foo condition={false} />);
314
+ ReactNoop.flush();
315
310
- ReactNoop.render(<Foo condition={false} />);
311
- ReactNoop.flush();
316
+ expect(ops).toEqual([]);
317
+ expect(ReactNoop.getChildren()).toEqual([div(), div()]);
318
313
- expect(ops).toEqual([]);
314
- expect(ReactNoop.getChildren()).toEqual([div(), div()]);
319
+ ReactNoop.render(<Foo condition={true} />);
320
+ ReactNoop.flush();
321
316
- ReactNoop.render(<Foo condition={true} />);
317
- ReactNoop.flush();
322
+ expect(ops).toEqual([]);
323
+ expect(ReactNoop.getChildren()).toEqual([div()]);
324
+ });
325
319
- expect(ops).toEqual([]);
320
- expect(ReactNoop.getChildren()).toEqual([div()]);
321
- });
326
+ it('should preserve state between array nested in fragment and fragment', function() {
327
+ var ops = [];
328
323
- it('should preserve state between array nested in fragment and fragment', function() {
324
- var ops = [];
329
+ class Stateful extends React.Component {
330
+ componentDidUpdate() {
331
+ ops.push('Update Stateful');
332
+ }
333
326
- class Stateful extends React.Component {
327
- componentDidUpdate() {
328
- ops.push('Update Stateful');
334
+ render() {
335
+ return <div>Hello</div>;
336
+ }
337
}
338
331
- render() {
332
- return <div>Hello</div>;
339
+ function Foo({condition}) {
340
+ return condition
341
+ ? <React.Fragment>
342
+ <Stateful key="a" />
343
+ </React.Fragment>
344
+ : <React.Fragment>
345
+ {[<Stateful key="a" />]}
346
+ </React.Fragment>;
347
}
334
- }
348
336
- function Foo({condition}) {
337
- return condition
338
- ? <React.Fragment>
339
- <Stateful key="a" />
340
- </React.Fragment>
341
- : <React.Fragment>
342
- {[<Stateful key="a" />]}
343
- </React.Fragment>;
344
- }
349
+ ReactNoop.render(<Foo condition={true} />);
350
+ ReactNoop.flush();
351
346
- ReactNoop.render(<Foo condition={true} />);
347
- ReactNoop.flush();
352
+ ReactNoop.render(<Foo condition={false} />);
353
+ ReactNoop.flush();
354
349
- ReactNoop.render(<Foo condition={false} />);
350
- ReactNoop.flush();
355
+ expect(ops).toEqual(['Update Stateful']);
356
+ expect(ReactNoop.getChildren()).toEqual([div()]);
357
352
- expect(ops).toEqual(['Update Stateful']);
353
- expect(ReactNoop.getChildren()).toEqual([div()]);
358
+ ReactNoop.render(<Foo condition={true} />);
359
+ ReactNoop.flush();
360
355
- ReactNoop.render(<Foo condition={true} />);
356
- ReactNoop.flush();
361
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
362
+ expect(ReactNoop.getChildren()).toEqual([div()]);
363
+ });
364
358
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
359
- expect(ReactNoop.getChildren()).toEqual([div()]);
360
- });
365
+ it('should preserve state between top level fragment and array', function() {
366
+ var ops = [];
367
362
- it('should preserve state between top level fragment and array', function() {
363
- var ops = [];
368
+ class Stateful extends React.Component {
369
+ componentDidUpdate() {
370
+ ops.push('Update Stateful');
371
+ }
372
365
- class Stateful extends React.Component {
366
- componentDidUpdate() {
367
- ops.push('Update Stateful');
373
+ render() {
374
+ return <div>Hello</div>;
375
+ }
376
}
377
370
- render() {
371
- return <div>Hello</div>;
378
+ function Foo({condition}) {
379
+ return condition
380
+ ? [<Stateful key="a" />]
381
+ : <React.Fragment>
382
+ <Stateful key="a" />
383
+ </React.Fragment>;
384
}
373
- }
385
375
- function Foo({condition}) {
376
- return condition
377
- ? [<Stateful key="a" />]
378
- : <React.Fragment>
379
- <Stateful key="a" />
380
- </React.Fragment>;
381
- }
386
+ ReactNoop.render(<Foo condition={true} />);
387
+ ReactNoop.flush();
388
383
- ReactNoop.render(<Foo condition={true} />);
384
- ReactNoop.flush();
389
+ ReactNoop.render(<Foo condition={false} />);
390
+ ReactNoop.flush();
391
386
- ReactNoop.render(<Foo condition={false} />);
387
- ReactNoop.flush();
392
+ expect(ops).toEqual(['Update Stateful']);
393
+ expect(ReactNoop.getChildren()).toEqual([div()]);
394
389
- expect(ops).toEqual(['Update Stateful']);
390
- expect(ReactNoop.getChildren()).toEqual([div()]);
395
+ ReactNoop.render(<Foo condition={true} />);
396
+ ReactNoop.flush();
397
392
- ReactNoop.render(<Foo condition={true} />);
393
- ReactNoop.flush();
398
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
399
+ expect(ReactNoop.getChildren()).toEqual([div()]);
400
+ });
401
395
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
396
- expect(ReactNoop.getChildren()).toEqual([div()]);
397
- });
402
+ it('should not preserve state between array nested in fragment and double nested fragment', function() {
403
+ var ops = [];
404
399
- it('should not preserve state between array nested in fragment and double nested fragment', function() {
400
- var ops = [];
405
+ class Stateful extends React.Component {
406
+ componentDidUpdate() {
407
+ ops.push('Update Stateful');
408
+ }
409
402
- class Stateful extends React.Component {
403
- componentDidUpdate() {
404
- ops.push('Update Stateful');
410
+ render() {
411
+ return <div>Hello</div>;
412
+ }
413
}
414
407
- render() {
408
- return <div>Hello</div>;
415
+ function Foo({condition}) {
416
+ return condition
417
+ ? <React.Fragment>{[<Stateful key="a" />]}</React.Fragment>
418
+ : <React.Fragment>
419
+ <React.Fragment><Stateful key="a" /></React.Fragment>
420
+ </React.Fragment>;
421
}
410
- }
422
412
- function Foo({condition}) {
413
- return condition
414
- ? <React.Fragment>{[<Stateful key="a" />]}</React.Fragment>
415
- : <React.Fragment>
416
- <React.Fragment><Stateful key="a" /></React.Fragment>
417
- </React.Fragment>;
418
- }
423
+ ReactNoop.render(<Foo condition={true} />);
424
+ ReactNoop.flush();
425
420
- ReactNoop.render(<Foo condition={true} />);
421
- ReactNoop.flush();
426
+ ReactNoop.render(<Foo condition={false} />);
427
+ ReactNoop.flush();
428
423
- ReactNoop.render(<Foo condition={false} />);
424
- ReactNoop.flush();
429
+ expect(ops).toEqual([]);
430
+ expect(ReactNoop.getChildren()).toEqual([div()]);
431
426
- expect(ops).toEqual([]);
427
- expect(ReactNoop.getChildren()).toEqual([div()]);
432
+ ReactNoop.render(<Foo condition={true} />);
433
+ ReactNoop.flush();
434
429
- ReactNoop.render(<Foo condition={true} />);
430
- ReactNoop.flush();
435
+ expect(ops).toEqual([]);
436
+ expect(ReactNoop.getChildren()).toEqual([div()]);
437
+ });
438
432
- expect(ops).toEqual([]);
433
- expect(ReactNoop.getChildren()).toEqual([div()]);
434
- });
439
+ it('should not preserve state between array nested in fragment and double nested array', function() {
440
+ var ops = [];
441
436
- it('should not preserve state between array nested in fragment and double nested array', function() {
437
- var ops = [];
442
+ class Stateful extends React.Component {
443
+ componentDidUpdate() {
444
+ ops.push('Update Stateful');
445
+ }
446
439
- class Stateful extends React.Component {
440
- componentDidUpdate() {
441
- ops.push('Update Stateful');
447
+ render() {
448
+ return <div>Hello</div>;
449
+ }
450
}
451
444
- render() {
445
- return <div>Hello</div>;
452
+ function Foo({condition}) {
453
+ return condition
454
+ ? <React.Fragment>{[<Stateful key="a" />]}</React.Fragment>
455
+ : [[<Stateful key="a" />]];
456
}
447
- }
457
449
- function Foo({condition}) {
450
- return condition
451
- ? <React.Fragment>{[<Stateful key="a" />]}</React.Fragment>
452
- : [[<Stateful key="a" />]];
453
- }
458
+ ReactNoop.render(<Foo condition={true} />);
459
+ ReactNoop.flush();
460
455
- ReactNoop.render(<Foo condition={true} />);
456
- ReactNoop.flush();
461
+ ReactNoop.render(<Foo condition={false} />);
462
+ ReactNoop.flush();
463
458
- ReactNoop.render(<Foo condition={false} />);
459
- ReactNoop.flush();
464
+ expect(ops).toEqual([]);
465
+ expect(ReactNoop.getChildren()).toEqual([div()]);
466
461
- expect(ops).toEqual([]);
462
- expect(ReactNoop.getChildren()).toEqual([div()]);
467
+ ReactNoop.render(<Foo condition={true} />);
468
+ ReactNoop.flush();
469
464
- ReactNoop.render(<Foo condition={true} />);
465
- ReactNoop.flush();
470
+ expect(ops).toEqual([]);
471
+ expect(ReactNoop.getChildren()).toEqual([div()]);
472
+ });
473
467
- expect(ops).toEqual([]);
468
- expect(ReactNoop.getChildren()).toEqual([div()]);
469
- });
474
+ it('should preserve state between double nested fragment and double nested array', function() {
475
+ var ops = [];
476
471
- it('should preserve state between double nested fragment and double nested array', function() {
472
- var ops = [];
477
+ class Stateful extends React.Component {
478
+ componentDidUpdate() {
479
+ ops.push('Update Stateful');
480
+ }
481
474
- class Stateful extends React.Component {
475
- componentDidUpdate() {
476
- ops.push('Update Stateful');
482
+ render() {
483
+ return <div>Hello</div>;
484
+ }
485
}
486
479
- render() {
480
- return <div>Hello</div>;
487
+ function Foo({condition}) {
488
+ return condition
489
+ ? <React.Fragment>
490
+ <React.Fragment><Stateful key="a" /></React.Fragment>
491
+ </React.Fragment>
492
+ : [[<Stateful key="a" />]];
493
}
482
- }
494
484
- function Foo({condition}) {
485
- return condition
486
- ? <React.Fragment>
487
- <React.Fragment><Stateful key="a" /></React.Fragment>
488
- </React.Fragment>
489
- : [[<Stateful key="a" />]];
490
- }
495
+ ReactNoop.render(<Foo condition={true} />);
496
+ ReactNoop.flush();
497
492
- ReactNoop.render(<Foo condition={true} />);
493
- ReactNoop.flush();
498
+ ReactNoop.render(<Foo condition={false} />);
499
+ ReactNoop.flush();
500
495
- ReactNoop.render(<Foo condition={false} />);
496
- ReactNoop.flush();
501
+ expect(ops).toEqual(['Update Stateful']);
502
+ expect(ReactNoop.getChildren()).toEqual([div()]);
503
498
- expect(ops).toEqual(['Update Stateful']);
499
- expect(ReactNoop.getChildren()).toEqual([div()]);
504
+ ReactNoop.render(<Foo condition={true} />);
505
+ ReactNoop.flush();
506
501
- ReactNoop.render(<Foo condition={true} />);
502
- ReactNoop.flush();
507
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
508
+ expect(ReactNoop.getChildren()).toEqual([div()]);
509
+ });
510
504
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
505
- expect(ReactNoop.getChildren()).toEqual([div()]);
506
- });
511
+ it('should not preserve state of children when the keys are different', function() {
512
+ var ops = [];
513
508
- it('should not preserve state of children when the keys are different', function() {
509
- var ops = [];
514
+ class Stateful extends React.Component {
515
+ componentDidUpdate() {
516
+ ops.push('Update Stateful');
517
+ }
518
511
- class Stateful extends React.Component {
512
- componentDidUpdate() {
513
- ops.push('Update Stateful');
519
+ render() {
520
+ return <div>Hello</div>;
521
+ }
522
}
523
516
- render() {
517
- return <div>Hello</div>;
524
+ function Foo({condition}) {
525
+ return condition
526
+ ? <React.Fragment key="a">
527
+ <Stateful />
528
+ </React.Fragment>
529
+ : <React.Fragment key="b">
530
+ <Stateful />
531
+ <span>World</span>
532
+ </React.Fragment>;
533
}
519
- }
534
521
- function Foo({condition}) {
522
- return condition
523
- ? <React.Fragment key="a">
524
- <Stateful />
525
- </React.Fragment>
526
- : <React.Fragment key="b">
527
- <Stateful />
528
- <span>World</span>
529
- </React.Fragment>;
530
- }
535
+ ReactNoop.render(<Foo condition={true} />);
536
+ ReactNoop.flush();
537
532
- ReactNoop.render(<Foo condition={true} />);
533
- ReactNoop.flush();
538
+ ReactNoop.render(<Foo condition={false} />);
539
+ ReactNoop.flush();
540
535
- ReactNoop.render(<Foo condition={false} />);
536
- ReactNoop.flush();
541
+ expect(ops).toEqual([]);
542
+ expect(ReactNoop.getChildren()).toEqual([div(), span()]);
543
538
- expect(ops).toEqual([]);
539
- expect(ReactNoop.getChildren()).toEqual([div(), span()]);
544
+ ReactNoop.render(<Foo condition={true} />);
545
+ ReactNoop.flush();
546
541
- ReactNoop.render(<Foo condition={true} />);
542
- ReactNoop.flush();
547
+ expect(ops).toEqual([]);
548
+ expect(ReactNoop.getChildren()).toEqual([div()]);
549
+ });
550
544
- expect(ops).toEqual([]);
545
- expect(ReactNoop.getChildren()).toEqual([div()]);
546
- });
551
+ it('should not preserve state between unkeyed and keyed fragment', function() {
552
+ var ops = [];
553
548
- it('should not preserve state between unkeyed and keyed fragment', function() {
549
- var ops = [];
554
+ class Stateful extends React.Component {
555
+ componentDidUpdate() {
556
+ ops.push('Update Stateful');
557
+ }
558
551
- class Stateful extends React.Component {
552
- componentDidUpdate() {
553
- ops.push('Update Stateful');
559
+ render() {
560
+ return <div>Hello</div>;
561
+ }
562
}
563
556
- render() {
557
- return <div>Hello</div>;
564
+ function Foo({condition}) {
565
+ return condition
566
+ ? <React.Fragment key="a">
567
+ <Stateful />
568
+ </React.Fragment>
569
+ : <React.Fragment>
570
+ <Stateful />
571
+ </React.Fragment>;
572
}
559
- }
573
561
- function Foo({condition}) {
562
- return condition
563
- ? <React.Fragment key="a">
564
- <Stateful />
565
- </React.Fragment>
566
- : <React.Fragment>
567
- <Stateful />
568
- </React.Fragment>;
569
- }
574
+ ReactNoop.render(<Foo condition={true} />);
575
+ ReactNoop.flush();
576
571
- ReactNoop.render(<Foo condition={true} />);
572
- ReactNoop.flush();
577
+ ReactNoop.render(<Foo condition={false} />);
578
+ ReactNoop.flush();
579
574
- ReactNoop.render(<Foo condition={false} />);
575
- ReactNoop.flush();
580
+ expect(ops).toEqual([]);
581
+ expect(ReactNoop.getChildren()).toEqual([div()]);
582
577
- expect(ops).toEqual([]);
578
- expect(ReactNoop.getChildren()).toEqual([div()]);
583
+ ReactNoop.render(<Foo condition={true} />);
584
+ ReactNoop.flush();
585
580
- ReactNoop.render(<Foo condition={true} />);
581
- ReactNoop.flush();
586
+ expect(ops).toEqual([]);
587
+ expect(ReactNoop.getChildren()).toEqual([div()]);
588
+ });
589
583
- expect(ops).toEqual([]);
584
- expect(ReactNoop.getChildren()).toEqual([div()]);
585
- });
590
+ it('should preserve state with reordering in multiple levels', function() {
591
+ var ops = [];
592
587
- it('should preserve state with reordering in multiple levels', function() {
588
- var ops = [];
593
+ class Stateful extends React.Component {
594
+ componentDidUpdate() {
595
+ ops.push('Update Stateful');
596
+ }
597
590
- class Stateful extends React.Component {
591
- componentDidUpdate() {
592
- ops.push('Update Stateful');
598
+ render() {
599
+ return <div>Hello</div>;
600
+ }
601
}
602
595
- render() {
596
- return <div>Hello</div>;
603
+ function Foo({condition}) {
604
+ return condition
605
+ ? <div>
606
+ <React.Fragment key="c">
607
+ <span>foo</span>
608
+ <div key="b">
609
+ <Stateful key="a" />
610
+ </div>
611
+ </React.Fragment>
612
+ <span>boop</span>
613
+ </div>
614
+ : <div>
615
+ <span>beep</span>
616
+ <React.Fragment key="c">
617
+ <div key="b">
618
+ <Stateful key="a" />
619
+ </div>
620
+ <span>bar</span>
621
+ </React.Fragment>
622
+ </div>;
623
}
598
- }
624
600
- function Foo({condition}) {
601
- return condition
602
- ? <div>
603
- <React.Fragment key="c">
604
- <span>foo</span>
605
- <div key="b">
606
- <Stateful key="a" />
607
- </div>
608
- </React.Fragment>
609
- <span>boop</span>
610
- </div>
611
- : <div>
612
- <span>beep</span>
613
- <React.Fragment key="c">
614
- <div key="b">
615
- <Stateful key="a" />
616
- </div>
617
- <span>bar</span>
618
- </React.Fragment>
619
- </div>;
620
- }
625
+ ReactNoop.render(<Foo condition={true} />);
626
+ ReactNoop.flush();
627
622
- ReactNoop.render(<Foo condition={true} />);
623
- ReactNoop.flush();
628
+ ReactNoop.render(<Foo condition={false} />);
629
+ ReactNoop.flush();
630
625
- ReactNoop.render(<Foo condition={false} />);
626
- ReactNoop.flush();
631
+ expect(ops).toEqual(['Update Stateful']);
632
+ expect(ReactNoop.getChildren()).toEqual([
633
+ div(span(), div(div()), span()),
634
+ ]);
635
628
- expect(ops).toEqual(['Update Stateful']);
629
- expect(ReactNoop.getChildren()).toEqual([div(span(), div(div()), span())]);
636
+ ReactNoop.render(<Foo condition={true} />);
637
+ ReactNoop.flush();
638
631
- ReactNoop.render(<Foo condition={true} />);
632
- ReactNoop.flush();
639
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
640
+ expect(ReactNoop.getChildren()).toEqual([
641
+ div(span(), div(div()), span()),
642
+ ]);
643
+ });
644
634
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
635
- expect(ReactNoop.getChildren()).toEqual([div(span(), div(div()), span())]);
636
- });
645
+ it('should not preserve state when switching to a keyed fragment to an array', function() {
646
+ spyOn(console, 'error');
647
+ var ops = [];
648
638
- it('should not preserve state when switching to a keyed fragment to an array', function() {
639
- spyOn(console, 'error');
640
- var ops = [];
649
+ class Stateful extends React.Component {
650
+ componentDidUpdate() {
651
+ ops.push('Update Stateful');
652
+ }
653
642
- class Stateful extends React.Component {
643
- componentDidUpdate() {
644
- ops.push('Update Stateful');
654
+ render() {
655
+ return <div>Hello</div>;
656
+ }
657
}
658
647
- render() {
648
- return <div>Hello</div>;
659
+ function Foo({condition}) {
660
+ return condition
661
+ ? <div>
662
+ {<React.Fragment key="foo"><Stateful /></React.Fragment>}<span />
663
+ </div>
664
+ : <div>{[<Stateful />]}<span /></div>;
665
}
650
- }
666
652
- function Foo({condition}) {
653
- return condition
654
- ? <div>
655
- {<React.Fragment key="foo"><Stateful /></React.Fragment>}<span />
656
- </div>
657
- : <div>{[<Stateful />]}<span /></div>;
658
- }
667
+ ReactNoop.render(<Foo condition={true} />);
668
+ ReactNoop.flush();
669
660
- ReactNoop.render(<Foo condition={true} />);
661
- ReactNoop.flush();
670
+ ReactNoop.render(<Foo condition={false} />);
671
+ ReactNoop.flush();
672
663
- ReactNoop.render(<Foo condition={false} />);
664
- ReactNoop.flush();
673
+ expect(ops).toEqual([]);
674
+ expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
675
666
- expect(ops).toEqual([]);
667
- expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
676
+ ReactNoop.render(<Foo condition={true} />);
677
+ ReactNoop.flush();
678
669
- ReactNoop.render(<Foo condition={true} />);
670
- ReactNoop.flush();
679
+ expect(ops).toEqual([]);
680
+ expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
681
+ expectDev(console.error.calls.count()).toBe(1);
682
+ expectDev(console.error.calls.argsFor(0)[0]).toContain(
683
+ 'Each child in an array or iterator should have a unique "key" prop.',
684
+ );
685
+ });
686
672
- expect(ops).toEqual([]);
673
- expect(ReactNoop.getChildren()).toEqual([div(div(), span())]);
674
- expectDev(console.error.calls.count()).toBe(1);
675
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
676
- 'Each child in an array or iterator should have a unique "key" prop.',
677
- );
678
- });
687
+ it('should preserve state when it does not change positions', function() {
688
+ spyOn(console, 'error');
689
+ var ops = [];
690
680
- it('should preserve state when it does not change positions', function() {
681
- spyOn(console, 'error');
682
- var ops = [];
691
+ class Stateful extends React.Component {
692
+ componentDidUpdate() {
693
+ ops.push('Update Stateful');
694
+ }
695
684
- class Stateful extends React.Component {
685
- componentDidUpdate() {
686
- ops.push('Update Stateful');
696
+ render() {
697
+ return <div>Hello</div>;
698
+ }
699
}
700
689
- render() {
690
- return <div>Hello</div>;
701
+ function Foo({condition}) {
702
+ return condition
703
+ ? [<span />, <React.Fragment><Stateful /></React.Fragment>]
704
+ : [<span />, <React.Fragment><Stateful /></React.Fragment>];
705
}
692
- }
706
694
- function Foo({condition}) {
695
- return condition
696
- ? [<span />, <React.Fragment><Stateful /></React.Fragment>]
697
- : [<span />, <React.Fragment><Stateful /></React.Fragment>];
698
- }
707
+ ReactNoop.render(<Foo condition={true} />);
708
+ ReactNoop.flush();
709
700
- ReactNoop.render(<Foo condition={true} />);
701
- ReactNoop.flush();
710
+ ReactNoop.render(<Foo condition={false} />);
711
+ ReactNoop.flush();
712
703
- ReactNoop.render(<Foo condition={false} />);
704
- ReactNoop.flush();
713
+ expect(ops).toEqual(['Update Stateful']);
714
+ expect(ReactNoop.getChildren()).toEqual([span(), div()]);
715
706
- expect(ops).toEqual(['Update Stateful']);
707
- expect(ReactNoop.getChildren()).toEqual([span(), div()]);
716
+ ReactNoop.render(<Foo condition={true} />);
717
+ ReactNoop.flush();
718
709
- ReactNoop.render(<Foo condition={true} />);
710
- ReactNoop.flush();
711
-
712
- expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
713
- expect(ReactNoop.getChildren()).toEqual([span(), div()]);
714
- expectDev(console.error.calls.count()).toBe(3);
715
- for (let errorIndex = 0; errorIndex < 3; ++errorIndex) {
716
- expectDev(console.error.calls.argsFor(errorIndex)[0]).toContain(
717
- 'Each child in an array or iterator should have a unique "key" prop.',
718
- );
719
- }
720
- });
719
+ expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
720
+ expect(ReactNoop.getChildren()).toEqual([span(), div()]);
721
+ expectDev(console.error.calls.count()).toBe(3);
722
+ for (let errorIndex = 0; errorIndex < 3; ++errorIndex) {
723
+ expectDev(console.error.calls.argsFor(errorIndex)[0]).toContain(
724
+ 'Each child in an array or iterator should have a unique "key" prop.',
725
+ );
726
+ }
727
+ });
728
+ } else {
729
+ it('should not run any tests', function() {});
730
+ }
731
});
packages/react/src/React.js
+5
-1
@@ -8,6 +8,7 @@
8
'use strict';
9
10
var ReactVersion = require('shared/ReactVersion');
11
+var ReactFeatureFlags = require('shared/ReactFeatureFlags');
12
13
var ReactBaseClasses = require('./ReactBaseClasses');
14
var ReactChildren = require('./ReactChildren');
@@ -43,7 +44,6 @@ var React = {
44
Component: ReactBaseClasses.Component,
45
PureComponent: ReactBaseClasses.PureComponent,
46
unstable_AsyncComponent: ReactBaseClasses.AsyncComponent,
46
- Fragment: REACT_FRAGMENT_TYPE,
47
48
createElement: createElement,
49
cloneElement: cloneElement,
@@ -60,6 +60,10 @@ var React = {
60
},
61
};
62
63
+if (ReactFeatureFlags.enableReactFragment) {
64
+ React.Fragment = REACT_FRAGMENT_TYPE;
65
+}
66
+
67
if (__DEV__) {
68
Object.assign(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
69
// These should not be included in production.
packages/react/src/__tests__/ReactJSXElementValidator-test.js
+64
-58
@@ -9,6 +9,8 @@
9
10
'use strict';
11
12
+var ReactFeatureFlags = require('shared/ReactFeatureFlags');
13
+
14
// TODO: All these warnings should become static errors using Flow instead
15
// of dynamic errors when using JSX with Flow.
16
var React;
@@ -85,48 +87,50 @@ describe('ReactJSXElementValidator', () => {
87
);
88
});
89
88
- it('warns for fragments with illegal attributes', () => {
89
- spyOn(console, 'error');
90
+ if (ReactFeatureFlags.enableReactFragment) {
91
+ it('warns for fragments with illegal attributes', () => {
92
+ spyOn(console, 'error');
93
91
- class Foo extends React.Component {
92
- render() {
93
- return <React.Fragment a={1} b={2}>hello</React.Fragment>;
94
+ class Foo extends React.Component {
95
+ render() {
96
+ return <React.Fragment a={1} b={2}>hello</React.Fragment>;
97
+ }
98
}
95
- }
96
-
97
- ReactTestUtils.renderIntoDocument(<Foo />);
99
99
- expectDev(console.error.calls.count()).toBe(1);
100
- expectDev(console.error.calls.argsFor(0)[0]).toContain('Invalid prop `');
101
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
102
- '` supplied to `React.Fragment`. React.Fragment ' +
103
- 'can only have `key` and `children` props.',
104
- );
105
- });
100
+ ReactTestUtils.renderIntoDocument(<Foo />);
101
107
- it('warns for fragments with refs', () => {
108
- spyOn(console, 'error');
102
+ expectDev(console.error.calls.count()).toBe(1);
103
+ expectDev(console.error.calls.argsFor(0)[0]).toContain('Invalid prop `');
104
+ expectDev(console.error.calls.argsFor(0)[0]).toContain(
105
+ '` supplied to `React.Fragment`. React.Fragment ' +
106
+ 'can only have `key` and `children` props.',
107
+ );
108
+ });
109
110
- class Foo extends React.Component {
111
- render() {
112
- return (
113
- <React.Fragment
114
- ref={bar => {
115
- this.foo = bar;
116
- }}>
117
- hello
118
- </React.Fragment>
119
- );
110
+ it('warns for fragments with refs', () => {
111
+ spyOn(console, 'error');
112
+
113
+ class Foo extends React.Component {
114
+ render() {
115
+ return (
116
+ <React.Fragment
117
+ ref={bar => {
118
+ this.foo = bar;
119
+ }}>
120
+ hello
121
+ </React.Fragment>
122
+ );
123
+ }
124
}
121
- }
125
123
- ReactTestUtils.renderIntoDocument(<Foo />);
126
+ ReactTestUtils.renderIntoDocument(<Foo />);
127
125
- expectDev(console.error.calls.count()).toBe(1);
126
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
127
- 'Invalid attribute `ref` supplied to `React.Fragment`.',
128
- );
129
- });
128
+ expectDev(console.error.calls.count()).toBe(1);
129
+ expectDev(console.error.calls.argsFor(0)[0]).toContain(
130
+ 'Invalid attribute `ref` supplied to `React.Fragment`.',
131
+ );
132
+ });
133
+ }
134
135
it('warns for keys for iterables of elements in rest args', () => {
136
spyOn(console, 'error');
@@ -151,31 +155,33 @@ describe('ReactJSXElementValidator', () => {
155
);
156
});
157
154
- it('does not warn for fragments of multiple elements without keys', () => {
155
- ReactTestUtils.renderIntoDocument(
156
- <React.Fragment>
157
- <span>1</span>
158
- <span>2</span>
159
- </React.Fragment>,
160
- );
161
- });
162
-
163
- it('warns for fragments of multiple elements with same key', () => {
164
- spyOn(console, 'error');
165
-
166
- ReactTestUtils.renderIntoDocument(
167
- <React.Fragment>
168
- <span key="a">1</span>
169
- <span key="a">2</span>
170
- <span key="b">3</span>
171
- </React.Fragment>,
172
- );
158
+ if (ReactFeatureFlags.enableReactFragment) {
159
+ it('does not warn for fragments of multiple elements without keys', () => {
160
+ ReactTestUtils.renderIntoDocument(
161
+ <React.Fragment>
162
+ <span>1</span>
163
+ <span>2</span>
164
+ </React.Fragment>,
165
+ );
166
+ });
167
174
- expectDev(console.error.calls.count()).toBe(1);
175
- expectDev(console.error.calls.argsFor(0)[0]).toContain(
176
- 'Encountered two children with the same key, `a`.',
177
- );
178
- });
168
+ it('warns for fragments of multiple elements with same key', () => {
169
+ spyOn(console, 'error');
170
+
171
+ ReactTestUtils.renderIntoDocument(
172
+ <React.Fragment>
173
+ <span key="a">1</span>
174
+ <span key="a">2</span>
175
+ <span key="b">3</span>
176
+ </React.Fragment>,
177
+ );
178
+
179
+ expectDev(console.error.calls.count()).toBe(1);
180
+ expectDev(console.error.calls.argsFor(0)[0]).toContain(
181
+ 'Encountered two children with the same key, `a`.',
182
+ );
183
+ });
184
+ }
185
186
it('does not warn for arrays of elements with keys', () => {
187
spyOn(console, 'error');
packages/shared/ReactFeatureFlags.js
+3
@@ -15,6 +15,7 @@ export type FeatureFlags = {|
15
enableMutatingReconciler: boolean,
16
enableNoopReconciler: boolean,
17
enablePersistentReconciler: boolean,
18
+ enableReactFragment: boolean,
19
|};
20
21
var ReactFeatureFlags: FeatureFlags = {
@@ -26,6 +27,8 @@ var ReactFeatureFlags: FeatureFlags = {
27
enableNoopReconciler: false,
28
// Experimental persistent mode (CS):
29
enablePersistentReconciler: false,
30
+ // Exports React.Fragment
31
+ enableReactFragment: false,
32
};
33
34
if (__DEV__) {