Handle updates to natives and composites
Dan Abramov committed
Apr 20, 2016 at 16:36 UTC
7884606fdb4519cc0a062eb7cd98cfc889cfd8b7
6 files changed
+1593
-452
src/isomorphic/__tests__/ReactDebugTool-test.js
+1529
-422
@@ -12,36 +12,6 @@
12
'use strict';
13
14
describe('ReactDebugTool', () => {
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
- /*
28
- TODO:
29
- * Make sure update and unmount paths are tested
30
- * Make sure server rendering works the same way
31
- * Enable all existing tests
32
- * Remove ReactInstanceMap usage
33
- */
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
15
var React;
16
var ReactDebugTool;
17
var ReactDOM;
@@ -59,7 +29,25 @@ describe('ReactDebugTool', () => {
29
update(tree[debugID]);
30
}
31
62
- var devtool = {
32
+ function getTree(debugID, includeOwner) {
33
+ var item = tree[debugID];
34
+ var result = {
35
+ isComposite: item.isComposite,
36
+ displayName: item.displayName,
37
+ };
38
+ result.children = item.childDebugIDs.map(childDebugID =>
39
+ getTree(childDebugID, includeOwner)
40
+ );
41
+ if (item.text != null) {
42
+ result.text = item.text;
43
+ }
44
+ if (includeOwner && item.ownerDebugID) {
45
+ result.ownerDisplayName = tree[item.ownerDebugID].displayName;
46
+ }
47
+ return result;
48
+ }
49
+
50
+ return {
51
onSetIsComposite(debugID, isComposite) {
52
updateTree(debugID, item => item.isComposite = isComposite);
53
},
@@ -75,26 +63,10 @@ describe('ReactDebugTool', () => {
63
onSetText(debugID, text) {
64
updateTree(debugID, item => item.text = text);
65
},
78
- getTree(debugID, includeOwner) {
79
- var item = tree[debugID];
80
- var result = {
81
- isComposite: item.isComposite,
82
- displayName: item.displayName,
83
- };
84
- result.children = item.childDebugIDs.map(childDebugID =>
85
- devtool.getTree(childDebugID, includeOwner)
86
- );
87
- if (item.text != null) {
88
- result.text = item.text;
89
- }
90
- if (includeOwner && item.ownerDebugID) {
91
- result.ownerDisplayName = tree[item.ownerDebugID].displayName;
92
- }
93
- return result;
94
- }
66
+ getTree(rootDebugID, includeOwner) {
67
+ return getTree(rootDebugID, includeOwner);
68
+ },
69
};
96
-
97
- return devtool;
70
}
71
72
beforeEach(() => {
@@ -113,446 +85,1581 @@ describe('ReactDebugTool', () => {
85
ReactDebugTool.removeDevtool(devtool);
86
});
87
116
- function assertTreeMatches(element, expectedTree, includeOwner) {
88
+ function assertTreeMatches(pairs, includeOwner) {
89
+ if (!Array.isArray(pairs[0])) {
90
+ pairs = [pairs];
91
+ }
92
+
93
+ var currentElement;
94
class Wrapper extends React.Component {
95
render() {
119
- return element;
96
+ return currentElement;
97
}
98
}
99
100
var node = document.createElement('div');
124
- var rootPublicInstance = ReactDOM.render(<Wrapper />, node);
125
- var rootInstance = ReactInstanceMap.get(rootPublicInstance);
126
- var actualTree = devtool.getTree(
127
- rootInstance._renderedComponent._debugID,
128
- includeOwner
129
- );
130
- expect(actualTree).toEqual(expectedTree);
131
- }
132
-
133
- it('uses displayName or Unknown for classic components', () => {
134
- var Foo = React.createClass({
135
- render() {
136
- return null;
137
- },
138
- });
139
- Foo.displayName = 'Bar';
140
- var Baz = React.createClass({
141
- render() {
142
- return null;
143
- },
144
- });
145
- var Qux = React.createClass({
146
- render() {
147
- return null;
148
- },
149
- });
150
- delete Qux.displayName;
151
-
152
- var element = <div><Foo /><Baz /><Qux /></div>;
153
- var expectedTree = {
154
- isComposite: false,
155
- displayName: 'div',
156
- children: [{
157
- isComposite: true,
158
- displayName: 'Bar',
159
- children: [],
160
- }, {
161
- isComposite: true,
162
- displayName: 'Baz',
163
- children: [],
164
- }, {
165
- isComposite: true,
166
- displayName: 'Unknown',
167
- children: [],
168
- }],
169
- };
170
- assertTreeMatches(element, expectedTree);
171
- });
101
173
- it('uses displayName, name, or ReactComponent for modern components', () => {
174
- class Foo extends React.Component {
175
- render() {
176
- return null;
177
- }
178
- }
179
- Foo.displayName = 'Bar';
180
- class Baz extends React.Component {
181
- render() {
182
- return null;
183
- }
184
- }
185
- class Qux extends React.Component {
186
- render() {
187
- return null;
188
- }
189
- }
190
- delete Qux.name;
102
+ pairs.forEach(([element, expectedTree]) => {
103
+ currentElement = element;
104
192
- var element = <div><Foo /><Baz /><Qux /></div>;
193
- var expectedTree = {
194
- isComposite: false,
195
- displayName: 'div',
196
- children: [{
197
- isComposite: true,
198
- displayName: 'Bar',
199
- children: [],
200
- }, {
201
- isComposite: true,
202
- displayName: 'Baz',
203
- children: [],
204
- }, {
205
- isComposite: true,
206
- // Note: Ideally fallback name should be consistent (e.g. "Unknown")
207
- displayName: 'ReactComponent',
208
- children: [],
209
- }],
210
- };
211
- assertTreeMatches(element, expectedTree);
212
- });
105
+ var rootPublicInstance = ReactDOM.render(<Wrapper />, node);
106
+ var rootInstance = ReactInstanceMap.get(rootPublicInstance);
107
+ var actualTree = devtool.getTree(
108
+ rootInstance._renderedComponent._debugID,
109
+ includeOwner
110
+ );
111
+ expect(actualTree).toEqual(expectedTree);
112
+ });
113
+ }
114
214
- it('uses displayName, name, or Object for factory components', () => {
215
- function Foo() {
216
- return {
115
+ describe('mount', () => {
116
+ it('uses displayName or Unknown for classic components', () => {
117
+ var Foo = React.createClass({
118
render() {
119
return null;
120
},
220
- };
221
- }
222
- Foo.displayName = 'Bar';
223
- function Baz() {
224
- return {
121
+ });
122
+ Foo.displayName = 'Bar';
123
+ var Baz = React.createClass({
124
render() {
125
return null;
126
},
228
- };
229
- }
230
- function Qux() {
231
- return {
127
+ });
128
+ var Qux = React.createClass({
129
render() {
130
return null;
131
},
235
- };
236
- }
237
- delete Qux.name;
132
+ });
133
+ delete Qux.displayName;
134
239
- var element = <div><Foo /><Baz /><Qux /></div>;
240
- var expectedTree = {
241
- isComposite: false,
242
- displayName: 'div',
243
- children: [{
244
- isComposite: true,
245
- displayName: 'Bar',
246
- children: [],
247
- }, {
248
- isComposite: true,
249
- displayName: 'Baz',
250
- children: [],
251
- }, {
252
- isComposite: true,
253
- displayName: 'Unknown',
254
- children: [],
255
- }],
256
- };
257
- assertTreeMatches(element, expectedTree);
258
- });
259
-
260
- it('uses displayName, name, or StatelessComponent for functional components', () => {
261
- function Foo() {
262
- return null;
263
- }
264
- Foo.displayName = 'Bar';
265
- function Baz() {
266
- return null;
267
- }
268
- function Qux() {
269
- return null;
270
- }
271
- delete Qux.name;
135
+ var element = <div><Foo /><Baz /><Qux /></div>;
136
+ var tree = {
137
+ isComposite: false,
138
+ displayName: 'div',
139
+ children: [{
140
+ isComposite: true,
141
+ displayName: 'Bar',
142
+ children: [],
143
+ }, {
144
+ isComposite: true,
145
+ displayName: 'Baz',
146
+ children: [],
147
+ }, {
148
+ isComposite: true,
149
+ displayName: 'Unknown',
150
+ children: [],
151
+ }],
152
+ };
153
+ assertTreeMatches([element, tree]);
154
+ });
155
273
- var element = <div><Foo /><Baz /><Qux /></div>;
274
- var expectedTree = {
275
- isComposite: false,
276
- displayName: 'div',
277
- children: [{
278
- isComposite: true,
279
- displayName: 'Bar',
280
- children: [],
281
- }, {
282
- isComposite: true,
283
- displayName: 'Baz',
284
- children: [],
285
- }, {
286
- isComposite: true,
287
- displayName: 'Unknown',
288
- children: [],
289
- }],
290
- };
291
- assertTreeMatches(element, expectedTree);
292
- });
156
+ it('uses displayName, name, or ReactComponent for modern components', () => {
157
+ class Foo extends React.Component {
158
+ render() {
159
+ return null;
160
+ }
161
+ }
162
+ Foo.displayName = 'Bar';
163
+ class Baz extends React.Component {
164
+ render() {
165
+ return null;
166
+ }
167
+ }
168
+ class Qux extends React.Component {
169
+ render() {
170
+ return null;
171
+ }
172
+ }
173
+ delete Qux.name;
174
294
- it('reports a native tree correctly', () => {
295
- var element = (
296
- <div>
297
- <p>
298
- <span>
299
- Hi!
300
- </span>
301
- Wow.
302
- </p>
303
- <hr />
304
- </div>
305
- );
306
- var expectedTree = {
307
- isComposite: false,
308
- displayName: 'div',
309
- children: [{
175
+ var element = <div><Foo /><Baz /><Qux /></div>;
176
+ var tree = {
177
isComposite: false,
311
- displayName: 'p',
178
+ displayName: 'div',
179
children: [{
313
- isComposite: false,
314
- displayName: 'span',
315
- children: [{
316
- isComposite: false,
317
- displayName: '#text',
318
- text: 'Hi!',
319
- children: [],
320
- }],
180
+ isComposite: true,
181
+ displayName: 'Bar',
182
+ children: [],
183
}, {
322
- isComposite: false,
323
- displayName: '#text',
324
- text: 'Wow.',
184
+ isComposite: true,
185
+ displayName: 'Baz',
186
+ children: [],
187
+ }, {
188
+ isComposite: true,
189
+ // Note: Ideally fallback name should be consistent (e.g. "Unknown")
190
+ displayName: 'ReactComponent',
191
children: [],
192
}],
327
- }, {
328
- isComposite: false,
329
- displayName: 'hr',
330
- children: [],
331
- }],
332
- };
333
- assertTreeMatches(element, expectedTree);
334
- });
193
+ };
194
+ assertTreeMatches([element, tree]);
195
+ });
196
336
- it('reports a simple tree with composites correctly', () => {
337
- class Foo extends React.Component {
338
- render() {
339
- return <div />;
197
+ it('uses displayName, name, or Object for factory components', () => {
198
+ function Foo() {
199
+ return {
200
+ render() {
201
+ return null;
202
+ },
203
+ };
204
}
341
- }
205
+ Foo.displayName = 'Bar';
206
+ function Baz() {
207
+ return {
208
+ render() {
209
+ return null;
210
+ },
211
+ };
212
+ }
213
+ function Qux() {
214
+ return {
215
+ render() {
216
+ return null;
217
+ },
218
+ };
219
+ }
220
+ delete Qux.name;
221
343
- var element = <Foo />;
344
- var expectedTree = {
345
- isComposite: true,
346
- displayName: 'Foo',
347
- children: [{
222
+ var element = <div><Foo /><Baz /><Qux /></div>;
223
+ var tree = {
224
isComposite: false,
225
displayName: 'div',
350
- children: [],
351
- }],
352
- };
353
- assertTreeMatches(element, expectedTree);
354
- });
226
+ children: [{
227
+ isComposite: true,
228
+ displayName: 'Bar',
229
+ children: [],
230
+ }, {
231
+ isComposite: true,
232
+ displayName: 'Baz',
233
+ children: [],
234
+ }, {
235
+ isComposite: true,
236
+ displayName: 'Unknown',
237
+ children: [],
238
+ }],
239
+ };
240
+ assertTreeMatches([element, tree]);
241
+ });
242
356
- it('reports a tree with composites correctly', () => {
357
- var Qux = React.createClass({
358
- render() {
243
+ it('uses displayName, name, or StatelessComponent for functional components', () => {
244
+ function Foo() {
245
return null;
360
- },
361
- });
362
- function Foo() {
363
- return {
364
- render() {
365
- return <Qux />;
366
- },
367
- };
368
- }
369
- function Bar({children}) {
370
- return <h1>{children}</h1>;
371
- }
372
- class Baz extends React.Component {
373
- render() {
374
- return (
375
- <div>
376
- <Foo />
377
- <Bar>
378
- <span>Hi,</span>
379
- Mom
380
- </Bar>
381
- <a href="#">Click me.</a>
382
- </div>
383
- );
246
}
385
- }
247
+ Foo.displayName = 'Bar';
248
+ function Baz() {
249
+ return null;
250
+ }
251
+ function Qux() {
252
+ return null;
253
+ }
254
+ delete Qux.name;
255
387
- var element = <Baz />;
388
- var expectedTree = {
389
- isComposite: true,
390
- displayName: 'Baz',
391
- children: [{
256
+ var element = <div><Foo /><Baz /><Qux /></div>;
257
+ var tree = {
258
isComposite: false,
259
displayName: 'div',
260
children: [{
261
isComposite: true,
396
- displayName: 'Foo',
397
- children: [{
398
- isComposite: true,
399
- displayName: 'Qux',
400
- children: [],
401
- }],
262
+ displayName: 'Bar',
263
+ children: [],
264
}, {
265
isComposite: true,
404
- displayName: 'Bar',
266
+ displayName: 'Baz',
267
+ children: [],
268
+ }, {
269
+ isComposite: true,
270
+ displayName: 'Unknown',
271
+ children: [],
272
+ }],
273
+ };
274
+ assertTreeMatches([element, tree]);
275
+ });
276
+
277
+ it('reports a native tree correctly', () => {
278
+ var element = (
279
+ <div>
280
+ <p>
281
+ <span>
282
+ Hi!
283
+ </span>
284
+ Wow.
285
+ </p>
286
+ <hr />
287
+ </div>
288
+ );
289
+ var tree = {
290
+ isComposite: false,
291
+ displayName: 'div',
292
+ children: [{
293
+ isComposite: false,
294
+ displayName: 'p',
295
children: [{
296
isComposite: false,
407
- displayName: 'h1',
297
+ displayName: 'span',
298
children: [{
409
- isComposite: false,
410
- displayName: 'span',
411
- children: [{
412
- isComposite: false,
413
- displayName: '#text',
414
- text: 'Hi,',
415
- children: [],
416
- }],
417
- }, {
299
isComposite: false,
300
displayName: '#text',
420
- text: 'Mom',
301
+ text: 'Hi!',
302
children: [],
303
}],
423
- }],
424
- }, {
425
- isComposite: false,
426
- displayName: 'a',
427
- children: [{
304
+ }, {
305
isComposite: false,
306
displayName: '#text',
430
- text: 'Click me.',
307
+ text: 'Wow.',
308
children: [],
309
}],
433
- }],
434
- }],
435
- };
436
- assertTreeMatches(element, expectedTree);
437
- });
310
+ }, {
311
+ isComposite: false,
312
+ displayName: 'hr',
313
+ children: [],
314
+ }],
315
+ };
316
+ assertTreeMatches([element, tree]);
317
+ });
318
439
- it('ignores null children', () => {
440
- class Foo extends React.Component {
441
- render() {
442
- return null;
319
+ it('reports a simple tree with composites correctly', () => {
320
+ class Foo extends React.Component {
321
+ render() {
322
+ return <div />;
323
+ }
324
}
444
- }
445
- var element = <Foo />;
446
- var expectedTree = {
447
- isComposite: true,
448
- displayName: 'Foo',
449
- children: [],
450
- };
451
- assertTreeMatches(element, expectedTree);
452
- });
325
454
- it('ignores false children', () => {
455
- class Foo extends React.Component {
456
- render() {
457
- return false;
326
+ var element = <Foo />;
327
+ var tree = {
328
+ isComposite: true,
329
+ displayName: 'Foo',
330
+ children: [{
331
+ isComposite: false,
332
+ displayName: 'div',
333
+ children: [],
334
+ }],
335
+ };
336
+ assertTreeMatches([element, tree]);
337
+ });
338
+
339
+ it('reports a tree with composites correctly', () => {
340
+ var Qux = React.createClass({
341
+ render() {
342
+ return null;
343
+ },
344
+ });
345
+ function Foo() {
346
+ return {
347
+ render() {
348
+ return <Qux />;
349
+ },
350
+ };
351
+ }
352
+ function Bar({children}) {
353
+ return <h1>{children}</h1>;
354
+ }
355
+ class Baz extends React.Component {
356
+ render() {
357
+ return (
358
+ <div>
359
+ <Foo />
360
+ <Bar>
361
+ <span>Hi,</span>
362
+ Mom
363
+ </Bar>
364
+ <a href="#">Click me.</a>
365
+ </div>
366
+ );
367
+ }
368
}
459
- }
460
- var element = <Foo />;
461
- var expectedTree = {
462
- isComposite: true,
463
- displayName: 'Foo',
464
- children: [],
465
- };
466
- assertTreeMatches(element, expectedTree);
467
- });
369
469
- it('reports text nodes as children', () => {
470
- var element = <div>{'1'}{2}</div>;
471
- var expectedTree = {
472
- isComposite: false,
473
- displayName: 'div',
474
- children: [{
475
- isComposite: false,
476
- displayName: '#text',
477
- text: '1',
370
+ var element = <Baz />;
371
+ var tree = {
372
+ isComposite: true,
373
+ displayName: 'Baz',
374
+ children: [{
375
+ isComposite: false,
376
+ displayName: 'div',
377
+ children: [{
378
+ isComposite: true,
379
+ displayName: 'Foo',
380
+ children: [{
381
+ isComposite: true,
382
+ displayName: 'Qux',
383
+ children: [],
384
+ }],
385
+ }, {
386
+ isComposite: true,
387
+ displayName: 'Bar',
388
+ children: [{
389
+ isComposite: false,
390
+ displayName: 'h1',
391
+ children: [{
392
+ isComposite: false,
393
+ displayName: 'span',
394
+ children: [{
395
+ isComposite: false,
396
+ displayName: '#text',
397
+ text: 'Hi,',
398
+ children: [],
399
+ }],
400
+ }, {
401
+ isComposite: false,
402
+ displayName: '#text',
403
+ text: 'Mom',
404
+ children: [],
405
+ }],
406
+ }],
407
+ }, {
408
+ isComposite: false,
409
+ displayName: 'a',
410
+ children: [{
411
+ isComposite: false,
412
+ displayName: '#text',
413
+ text: 'Click me.',
414
+ children: [],
415
+ }],
416
+ }],
417
+ }],
418
+ };
419
+ assertTreeMatches([element, tree]);
420
+ });
421
+
422
+ it('ignores null children', () => {
423
+ class Foo extends React.Component {
424
+ render() {
425
+ return null;
426
+ }
427
+ }
428
+ var element = <Foo />;
429
+ var tree = {
430
+ isComposite: true,
431
+ displayName: 'Foo',
432
children: [],
479
- }, {
480
- isComposite: false,
481
- displayName: '#text',
482
- text: 2,
433
+ };
434
+ assertTreeMatches([element, tree]);
435
+ });
436
+
437
+ it('ignores false children', () => {
438
+ class Foo extends React.Component {
439
+ render() {
440
+ return false;
441
+ }
442
+ }
443
+ var element = <Foo />;
444
+ var tree = {
445
+ isComposite: true,
446
+ displayName: 'Foo',
447
children: [],
484
- }],
485
- };
486
- assertTreeMatches(element, expectedTree);
487
- });
448
+ };
449
+ assertTreeMatches([element, tree]);
450
+ });
451
489
- it('reports a single text node as a child', () => {
490
- var element = <div>{'1'}</div>;
491
- var expectedTree = {
492
- isComposite: false,
493
- displayName: 'div',
494
- children: [{
452
+ it('reports text nodes as children', () => {
453
+ var element = <div>{'1'}{2}</div>;
454
+ var tree = {
455
isComposite: false,
496
- displayName: '#text',
497
- text: '1',
498
- children: [],
499
- }],
500
- };
501
- assertTreeMatches(element, expectedTree);
502
- });
456
+ displayName: 'div',
457
+ children: [{
458
+ isComposite: false,
459
+ displayName: '#text',
460
+ text: '1',
461
+ children: [],
462
+ }, {
463
+ isComposite: false,
464
+ displayName: '#text',
465
+ text: '2',
466
+ children: [],
467
+ }],
468
+ };
469
+ assertTreeMatches([element, tree]);
470
+ });
471
504
- it('reports a single number node as a child', () => {
505
- var element = <div>{42}</div>;
506
- var expectedTree = {
507
- isComposite: false,
508
- displayName: 'div',
509
- children: [{
472
+ it('reports a single text node as a child', () => {
473
+ var element = <div>{'1'}</div>;
474
+ var tree = {
475
isComposite: false,
511
- displayName: '#text',
512
- text: 42,
513
- children: [],
514
- }],
515
- };
516
- assertTreeMatches(element, expectedTree);
517
- });
476
+ displayName: 'div',
477
+ children: [{
478
+ isComposite: false,
479
+ displayName: '#text',
480
+ text: '1',
481
+ children: [],
482
+ }],
483
+ };
484
+ assertTreeMatches([element, tree]);
485
+ });
486
519
- it('skips empty nodes for multiple children', () => {
520
- function Foo() {
521
- return <div />;
522
- }
523
- var element = (
524
- <div>
525
- {'hi'}
526
- {false}
527
- {42}
528
- {null}
529
- <Foo />
530
- </div>
531
- );
532
- var expectedTree = {
533
- isComposite: false,
534
- displayName: 'div',
535
- children: [{
487
+ it('reports a single number node as a child', () => {
488
+ var element = <div>{42}</div>;
489
+ var tree = {
490
isComposite: false,
537
- displayName: '#text',
538
- text: 'hi',
539
- children: [],
540
- }, {
491
+ displayName: 'div',
492
+ children: [{
493
+ isComposite: false,
494
+ displayName: '#text',
495
+ text: '42',
496
+ children: [],
497
+ }],
498
+ };
499
+ assertTreeMatches([element, tree]);
500
+ });
501
+
502
+ it('reports a zero as a child', () => {
503
+ var element = <div>{0}</div>;
504
+ var tree = {
505
isComposite: false,
542
- displayName: '#text',
543
- text: 42,
544
- children: [],
545
- }, {
546
- isComposite: true,
547
- displayName: 'Foo',
506
+ displayName: 'div',
507
children: [{
508
isComposite: false,
550
- displayName: 'div',
509
+ displayName: '#text',
510
+ text: '0',
511
children: [],
512
}],
553
- }],
554
- };
555
- assertTreeMatches(element, expectedTree);
513
+ };
514
+ assertTreeMatches([element, tree]);
515
+ });
516
+
517
+ it('skips empty nodes for multiple children', () => {
518
+ function Foo() {
519
+ return <div />;
520
+ }
521
+ var element = (
522
+ <div>
523
+ {'hi'}
524
+ {false}
525
+ {42}
526
+ {null}
527
+ <Foo />
528
+ </div>
529
+ );
530
+ var tree = {
531
+ isComposite: false,
532
+ displayName: 'div',
533
+ children: [{
534
+ isComposite: false,
535
+ displayName: '#text',
536
+ text: 'hi',
537
+ children: [],
538
+ }, {
539
+ isComposite: false,
540
+ displayName: '#text',
541
+ text: '42',
542
+ children: [],
543
+ }, {
544
+ isComposite: true,
545
+ displayName: 'Foo',
546
+ children: [{
547
+ isComposite: false,
548
+ displayName: 'div',
549
+ children: [],
550
+ }],
551
+ }],
552
+ };
553
+ assertTreeMatches([element, tree]);
554
+ });
555
+ });
556
+
557
+ describe('update', () => {
558
+ describe('native component', () => {
559
+ it('updates text of a single text child', () => {
560
+ var elementBefore = <div>Hi.</div>;
561
+ var treeBefore = {
562
+ isComposite: false,
563
+ displayName: 'div',
564
+ children: [{
565
+ isComposite: false,
566
+ displayName: '#text',
567
+ text: 'Hi.',
568
+ children: [],
569
+ }],
570
+ };
571
+
572
+ var elementAfter = <div>Bye.</div>;
573
+ var treeAfter = {
574
+ isComposite: false,
575
+ displayName: 'div',
576
+ children: [{
577
+ isComposite: false,
578
+ displayName: '#text',
579
+ text: 'Bye.',
580
+ children: [],
581
+ }],
582
+ };
583
+
584
+ assertTreeMatches([
585
+ [elementBefore, treeBefore],
586
+ [elementAfter, treeAfter],
587
+ ]);
588
+ });
589
+
590
+ it('updates from no children to a single text child', () => {
591
+ var elementBefore = <div />;
592
+ var treeBefore = {
593
+ isComposite: false,
594
+ displayName: 'div',
595
+ children: [],
596
+ };
597
+
598
+ var elementAfter = <div>Hi.</div>;
599
+ var treeAfter = {
600
+ isComposite: false,
601
+ displayName: 'div',
602
+ children: [{
603
+ isComposite: false,
604
+ displayName: '#text',
605
+ text: 'Hi.',
606
+ children: [],
607
+ }],
608
+ };
609
+
610
+ assertTreeMatches([
611
+ [elementBefore, treeBefore],
612
+ [elementAfter, treeAfter],
613
+ ]);
614
+ });
615
+
616
+ it('updates from a single text child to no children', () => {
617
+ var elementBefore = <div>Hi.</div>;
618
+ var treeBefore = {
619
+ isComposite: false,
620
+ displayName: 'div',
621
+ children: [{
622
+ isComposite: false,
623
+ displayName: '#text',
624
+ text: 'Hi.',
625
+ children: [],
626
+ }],
627
+ };
628
+
629
+ var elementAfter = <div />;
630
+ var treeAfter = {
631
+ isComposite: false,
632
+ displayName: 'div',
633
+ children: [],
634
+ };
635
+
636
+ assertTreeMatches([
637
+ [elementBefore, treeBefore],
638
+ [elementAfter, treeAfter],
639
+ ]);
640
+ });
641
+
642
+ it('updates from no children to multiple text children', () => {
643
+ var elementBefore = <div />;
644
+ var treeBefore = {
645
+ isComposite: false,
646
+ displayName: 'div',
647
+ children: [],
648
+ };
649
+
650
+ var elementAfter = <div>{'Hi.'}{'Bye.'}</div>;
651
+ var treeAfter = {
652
+ isComposite: false,
653
+ displayName: 'div',
654
+ children: [{
655
+ isComposite: false,
656
+ displayName: '#text',
657
+ text: 'Hi.',
658
+ children: [],
659
+ }, {
660
+ isComposite: false,
661
+ displayName: '#text',
662
+ text: 'Bye.',
663
+ children: [],
664
+ }],
665
+ };
666
+
667
+ assertTreeMatches([
668
+ [elementBefore, treeBefore],
669
+ [elementAfter, treeAfter],
670
+ ]);
671
+ });
672
+
673
+ it('updates from multiple text children to no children', () => {
674
+ var elementBefore = <div>{'Hi.'}{'Bye.'}</div>;
675
+ var treeBefore = {
676
+ isComposite: false,
677
+ displayName: 'div',
678
+ children: [{
679
+ isComposite: false,
680
+ displayName: '#text',
681
+ text: 'Hi.',
682
+ children: [],
683
+ }, {
684
+ isComposite: false,
685
+ displayName: '#text',
686
+ text: 'Bye.',
687
+ children: [],
688
+ }],
689
+ };
690
+
691
+ var elementAfter = <div />;
692
+ var treeAfter = {
693
+ isComposite: false,
694
+ displayName: 'div',
695
+ children: [],
696
+ };
697
+
698
+ assertTreeMatches([
699
+ [elementBefore, treeBefore],
700
+ [elementAfter, treeAfter],
701
+ ]);
702
+ });
703
+
704
+ it('updates from one text child to multiple text children', () => {
705
+ var elementBefore = <div>Hi.</div>;
706
+ var treeBefore = {
707
+ isComposite: false,
708
+ displayName: 'div',
709
+ children: [{
710
+ isComposite: false,
711
+ displayName: '#text',
712
+ text: 'Hi.',
713
+ children: [],
714
+ }],
715
+ };
716
+
717
+ var elementAfter = <div>{'Hi.'}{'Bye.'}</div>;
718
+ var treeAfter = {
719
+ isComposite: false,
720
+ displayName: 'div',
721
+ children: [{
722
+ isComposite: false,
723
+ displayName: '#text',
724
+ text: 'Hi.',
725
+ children: [],
726
+ }, {
727
+ isComposite: false,
728
+ displayName: '#text',
729
+ text: 'Bye.',
730
+ children: [],
731
+ }],
732
+ };
733
+
734
+ assertTreeMatches([
735
+ [elementBefore, treeBefore],
736
+ [elementAfter, treeAfter],
737
+ ]);
738
+ });
739
+
740
+ it('updates from multiple text children to one text child', () => {
741
+ var elementBefore = <div>{'Hi.'}{'Bye.'}</div>;
742
+ var treeBefore = {
743
+ isComposite: false,
744
+ displayName: 'div',
745
+ children: [{
746
+ isComposite: false,
747
+ displayName: '#text',
748
+ text: 'Hi.',
749
+ children: [],
750
+ }, {
751
+ isComposite: false,
752
+ displayName: '#text',
753
+ text: 'Bye.',
754
+ children: [],
755
+ }],
756
+ };
757
+
758
+ var elementAfter = <div>Hi.</div>;
759
+ var treeAfter = {
760
+ isComposite: false,
761
+ displayName: 'div',
762
+ children: [{
763
+ isComposite: false,
764
+ displayName: '#text',
765
+ text: 'Hi.',
766
+ children: [],
767
+ }],
768
+ };
769
+ assertTreeMatches([
770
+ [elementBefore, treeBefore],
771
+ [elementAfter, treeAfter],
772
+ ]);
773
+ });
774
+
775
+ it('updates text nodes when reordering', () => {
776
+ var elementBefore = <div>{'Hi.'}{'Bye.'}</div>;
777
+ var treeBefore = {
778
+ isComposite: false,
779
+ displayName: 'div',
780
+ children: [{
781
+ isComposite: false,
782
+ displayName: '#text',
783
+ text: 'Hi.',
784
+ children: [],
785
+ }, {
786
+ isComposite: false,
787
+ displayName: '#text',
788
+ text: 'Bye.',
789
+ children: [],
790
+ }],
791
+ };
792
+
793
+ var elementAfter = <div>{'Bye.'}{'Hi.'}</div>;
794
+ var treeAfter = {
795
+ isComposite: false,
796
+ displayName: 'div',
797
+ children: [{
798
+ isComposite: false,
799
+ displayName: '#text',
800
+ text: 'Bye.',
801
+ children: [],
802
+ }, {
803
+ isComposite: false,
804
+ displayName: '#text',
805
+ text: 'Hi.',
806
+ children: [],
807
+ }],
808
+ };
809
+ assertTreeMatches([
810
+ [elementBefore, treeBefore],
811
+ [elementAfter, treeAfter],
812
+ ]);
813
+ });
814
+
815
+ it('updates native nodes when reordering with keys', () => {
816
+ var elementBefore = (
817
+ <div>
818
+ <div key="a">Hi.</div>
819
+ <div key="b">Bye.</div>
820
+ </div>
821
+ );
822
+ var treeBefore = {
823
+ isComposite: false,
824
+ displayName: 'div',
825
+ children: [{
826
+ isComposite: false,
827
+ displayName: 'div',
828
+ children: [{
829
+ isComposite: false,
830
+ displayName: '#text',
831
+ text: 'Hi.',
832
+ children: [],
833
+ }],
834
+ }, {
835
+ isComposite: false,
836
+ displayName: 'div',
837
+ children: [{
838
+ isComposite: false,
839
+ displayName: '#text',
840
+ text: 'Bye.',
841
+ children: [],
842
+ }],
843
+ }],
844
+ };
845
+
846
+ var elementAfter = (
847
+ <div>
848
+ <div key="b">Bye.</div>
849
+ <div key="a">Hi.</div>
850
+ </div>
851
+ );
852
+ var treeAfter = {
853
+ isComposite: false,
854
+ displayName: 'div',
855
+ children: [{
856
+ isComposite: false,
857
+ displayName: 'div',
858
+ children: [{
859
+ isComposite: false,
860
+ displayName: '#text',
861
+ text: 'Bye.',
862
+ children: [],
863
+ }],
864
+ }, {
865
+ isComposite: false,
866
+ displayName: 'div',
867
+ children: [{
868
+ isComposite: false,
869
+ displayName: '#text',
870
+ text: 'Hi.',
871
+ children: [],
872
+ }],
873
+ }],
874
+ };
875
+
876
+ assertTreeMatches([
877
+ [elementBefore, treeBefore],
878
+ [elementAfter, treeAfter],
879
+ ]);
880
+ });
881
+
882
+ it('updates native nodes when reordering without keys', () => {
883
+ var elementBefore = (
884
+ <div>
885
+ <div>Hi.</div>
886
+ <div>Bye.</div>
887
+ </div>
888
+ );
889
+ var treeBefore = {
890
+ isComposite: false,
891
+ displayName: 'div',
892
+ children: [{
893
+ isComposite: false,
894
+ displayName: 'div',
895
+ children: [{
896
+ isComposite: false,
897
+ displayName: '#text',
898
+ text: 'Hi.',
899
+ children: [],
900
+ }],
901
+ }, {
902
+ isComposite: false,
903
+ displayName: 'div',
904
+ children: [{
905
+ isComposite: false,
906
+ displayName: '#text',
907
+ text: 'Bye.',
908
+ children: [],
909
+ }],
910
+ }],
911
+ };
912
+
913
+ var elementAfter = (
914
+ <div>
915
+ <div>Bye.</div>
916
+ <div>Hi.</div>
917
+ </div>
918
+ );
919
+ var treeAfter = {
920
+ isComposite: false,
921
+ displayName: 'div',
922
+ children: [{
923
+ isComposite: false,
924
+ displayName: 'div',
925
+ children: [{
926
+ isComposite: false,
927
+ displayName: '#text',
928
+ text: 'Bye.',
929
+ children: [],
930
+ }],
931
+ }, {
932
+ isComposite: false,
933
+ displayName: 'div',
934
+ children: [{
935
+ isComposite: false,
936
+ displayName: '#text',
937
+ text: 'Hi.',
938
+ children: [],
939
+ }],
940
+ }],
941
+ };
942
+
943
+ assertTreeMatches([
944
+ [elementBefore, treeBefore],
945
+ [elementAfter, treeAfter],
946
+ ]);
947
+ });
948
+
949
+ it('updates a single composite child of a different type', () => {
950
+ function Foo() {
951
+ return null;
952
+ }
953
+
954
+ function Bar() {
955
+ return null;
956
+ }
957
+
958
+ var elementBefore = <div><Foo /></div>;
959
+ var treeBefore = {
960
+ isComposite: false,
961
+ displayName: 'div',
962
+ children: [{
963
+ isComposite: true,
964
+ displayName: 'Foo',
965
+ children: [],
966
+ }],
967
+ };
968
+
969
+ var elementAfter = <div><Bar /></div>;
970
+ var treeAfter = {
971
+ isComposite: false,
972
+ displayName: 'div',
973
+ children: [{
974
+ isComposite: true,
975
+ displayName: 'Bar',
976
+ children: [],
977
+ }],
978
+ };
979
+
980
+ assertTreeMatches([
981
+ [elementBefore, treeBefore],
982
+ [elementAfter, treeAfter],
983
+ ]);
984
+ });
985
+
986
+ it('updates a single composite child of the same type', () => {
987
+ function Foo({ children }) {
988
+ return children;
989
+ }
990
+
991
+ var elementBefore = <div><Foo><div /></Foo></div>;
992
+ var treeBefore = {
993
+ isComposite: false,
994
+ displayName: 'div',
995
+ children: [{
996
+ isComposite: true,
997
+ displayName: 'Foo',
998
+ children: [{
999
+ isComposite: false,
1000
+ displayName: 'div',
1001
+ children: [],
1002
+ }],
1003
+ }],
1004
+ };
1005
+
1006
+ var elementAfter = <div><Foo><span /></Foo></div>;
1007
+ var treeAfter = {
1008
+ isComposite: false,
1009
+ displayName: 'div',
1010
+ children: [{
1011
+ isComposite: true,
1012
+ displayName: 'Foo',
1013
+ children: [{
1014
+ isComposite: false,
1015
+ displayName: 'span',
1016
+ children: [],
1017
+ }],
1018
+ }],
1019
+ };
1020
+
1021
+ assertTreeMatches([
1022
+ [elementBefore, treeBefore],
1023
+ [elementAfter, treeAfter],
1024
+ ]);
1025
+ });
1026
+
1027
+ it('updates from no children to a single composite child', () => {
1028
+ function Foo() {
1029
+ return null;
1030
+ }
1031
+
1032
+ var elementBefore = <div />;
1033
+ var treeBefore = {
1034
+ isComposite: false,
1035
+ displayName: 'div',
1036
+ children: [],
1037
+ };
1038
+
1039
+ var elementAfter = <div><Foo /></div>;
1040
+ var treeAfter = {
1041
+ isComposite: false,
1042
+ displayName: 'div',
1043
+ children: [{
1044
+ isComposite: true,
1045
+ displayName: 'Foo',
1046
+ children: [],
1047
+ }],
1048
+ };
1049
+
1050
+ assertTreeMatches([
1051
+ [elementBefore, treeBefore],
1052
+ [elementAfter, treeAfter],
1053
+ ]);
1054
+ });
1055
+
1056
+ it('updates from a single composite child to no children', () => {
1057
+ function Foo() {
1058
+ return null;
1059
+ }
1060
+
1061
+ var elementBefore = <div><Foo /></div>;
1062
+ var treeBefore = {
1063
+ isComposite: false,
1064
+ displayName: 'div',
1065
+ children: [{
1066
+ isComposite: true,
1067
+ displayName: 'Foo',
1068
+ children: [],
1069
+ }],
1070
+ };
1071
+
1072
+ var elementAfter = <div />;
1073
+ var treeAfter = {
1074
+ isComposite: false,
1075
+ displayName: 'div',
1076
+ children: [],
1077
+ };
1078
+
1079
+ assertTreeMatches([
1080
+ [elementBefore, treeBefore],
1081
+ [elementAfter, treeAfter],
1082
+ ]);
1083
+ });
1084
+
1085
+ it('updates mixed children', () => {
1086
+ function Foo() {
1087
+ return <div />;
1088
+ }
1089
+ var element1 = (
1090
+ <div>
1091
+ {'hi'}
1092
+ {false}
1093
+ {42}
1094
+ {null}
1095
+ <Foo />
1096
+ </div>
1097
+ );
1098
+ var tree1 = {
1099
+ isComposite: false,
1100
+ displayName: 'div',
1101
+ children: [{
1102
+ isComposite: false,
1103
+ displayName: '#text',
1104
+ text: 'hi',
1105
+ children: [],
1106
+ }, {
1107
+ isComposite: false,
1108
+ displayName: '#text',
1109
+ text: '42',
1110
+ children: [],
1111
+ }, {
1112
+ isComposite: true,
1113
+ displayName: 'Foo',
1114
+ children: [{
1115
+ isComposite: false,
1116
+ displayName: 'div',
1117
+ children: [],
1118
+ }],
1119
+ }],
1120
+ };
1121
+
1122
+ var element2 = (
1123
+ <div>
1124
+ <Foo />
1125
+ {false}
1126
+ {'hi'}
1127
+ {null}
1128
+ </div>
1129
+ );
1130
+ var tree2 = {
1131
+ isComposite: false,
1132
+ displayName: 'div',
1133
+ children: [{
1134
+ isComposite: true,
1135
+ displayName: 'Foo',
1136
+ children: [{
1137
+ isComposite: false,
1138
+ displayName: 'div',
1139
+ children: [],
1140
+ }],
1141
+ }, {
1142
+ isComposite: false,
1143
+ displayName: '#text',
1144
+ text: 'hi',
1145
+ children: [],
1146
+ }],
1147
+ };
1148
+
1149
+ var element3 = (
1150
+ <div>
1151
+ <Foo />
1152
+ </div>
1153
+ );
1154
+ var tree3 = {
1155
+ isComposite: false,
1156
+ displayName: 'div',
1157
+ children: [{
1158
+ isComposite: true,
1159
+ displayName: 'Foo',
1160
+ children: [{
1161
+ isComposite: false,
1162
+ displayName: 'div',
1163
+ children: [],
1164
+ }],
1165
+ }],
1166
+ };
1167
+
1168
+ assertTreeMatches([
1169
+ [element1, tree1],
1170
+ [element2, tree2],
1171
+ [element3, tree3],
1172
+ ]);
1173
+ });
1174
+ });
1175
+
1176
+ describe('functional component', () => {
1177
+ it('updates with a native child', () => {
1178
+ function Foo({ children }) {
1179
+ return children;
1180
+ }
1181
+
1182
+ var elementBefore = <Foo><div /></Foo>;
1183
+ var treeBefore = {
1184
+ isComposite: true,
1185
+ displayName: 'Foo',
1186
+ children: [{
1187
+ isComposite: false,
1188
+ displayName: 'div',
1189
+ children: [],
1190
+ }],
1191
+ };
1192
+
1193
+ var elementAfter = <Foo><span /></Foo>;
1194
+ var treeAfter = {
1195
+ isComposite: true,
1196
+ displayName: 'Foo',
1197
+ children: [{
1198
+ isComposite: false,
1199
+ displayName: 'span',
1200
+ children: [],
1201
+ }],
1202
+ };
1203
+
1204
+ assertTreeMatches([
1205
+ [elementBefore, treeBefore],
1206
+ [elementAfter, treeAfter],
1207
+ ]);
1208
+ });
1209
+
1210
+ it('updates from null to a native child', () => {
1211
+ function Foo({ children }) {
1212
+ return children;
1213
+ }
1214
+
1215
+ var elementBefore = <Foo>{null}</Foo>;
1216
+ var treeBefore = {
1217
+ isComposite: true,
1218
+ displayName: 'Foo',
1219
+ children: [],
1220
+ };
1221
+
1222
+ var elementAfter = <Foo><div /></Foo>;
1223
+ var treeAfter = {
1224
+ isComposite: true,
1225
+ displayName: 'Foo',
1226
+ children: [{
1227
+ isComposite: false,
1228
+ displayName: 'div',
1229
+ children: [],
1230
+ }],
1231
+ };
1232
+
1233
+ assertTreeMatches([
1234
+ [elementBefore, treeBefore],
1235
+ [elementAfter, treeAfter],
1236
+ ]);
1237
+ });
1238
+
1239
+ it('updates from a native child to null', () => {
1240
+ function Foo({ children }) {
1241
+ return children;
1242
+ }
1243
+
1244
+ var elementBefore = <Foo><div /></Foo>;
1245
+ var treeBefore = {
1246
+ isComposite: true,
1247
+ displayName: 'Foo',
1248
+ children: [{
1249
+ isComposite: false,
1250
+ displayName: 'div',
1251
+ children: [],
1252
+ }],
1253
+ };
1254
+
1255
+ var elementAfter = <Foo>{null}</Foo>;
1256
+ var treeAfter = {
1257
+ isComposite: true,
1258
+ displayName: 'Foo',
1259
+ children: [],
1260
+ };
1261
+
1262
+ assertTreeMatches([
1263
+ [elementBefore, treeBefore],
1264
+ [elementAfter, treeAfter],
1265
+ ]);
1266
+ });
1267
+
1268
+ it('updates from a native child to a composite child', () => {
1269
+ function Bar() {
1270
+ return null;
1271
+ }
1272
+
1273
+ function Foo({ children }) {
1274
+ return children;
1275
+ }
1276
+
1277
+ var elementBefore = <Foo><div /></Foo>;
1278
+ var treeBefore = {
1279
+ isComposite: true,
1280
+ displayName: 'Foo',
1281
+ children: [{
1282
+ isComposite: false,
1283
+ displayName: 'div',
1284
+ children: [],
1285
+ }],
1286
+ };
1287
+
1288
+ var elementAfter = <Foo><Bar /></Foo>;
1289
+ var treeAfter = {
1290
+ isComposite: true,
1291
+ displayName: 'Foo',
1292
+ children: [{
1293
+ isComposite: true,
1294
+ displayName: 'Bar',
1295
+ children: [],
1296
+ }],
1297
+ };
1298
+
1299
+ assertTreeMatches([
1300
+ [elementBefore, treeBefore],
1301
+ [elementAfter, treeAfter],
1302
+ ]);
1303
+ });
1304
+
1305
+ it('updates from a composite child to a native child', () => {
1306
+ function Bar() {
1307
+ return null;
1308
+ }
1309
+
1310
+ function Foo({ children }) {
1311
+ return children;
1312
+ }
1313
+
1314
+ var elementBefore = <Foo><Bar /></Foo>;
1315
+ var treeBefore = {
1316
+ isComposite: true,
1317
+ displayName: 'Foo',
1318
+ children: [{
1319
+ isComposite: true,
1320
+ displayName: 'Bar',
1321
+ children: [],
1322
+ }],
1323
+ };
1324
+
1325
+ var elementAfter = <Foo><div /></Foo>;
1326
+ var treeAfter = {
1327
+ isComposite: true,
1328
+ displayName: 'Foo',
1329
+ children: [{
1330
+ isComposite: false,
1331
+ displayName: 'div',
1332
+ children: [],
1333
+ }],
1334
+ };
1335
+
1336
+ assertTreeMatches([
1337
+ [elementBefore, treeBefore],
1338
+ [elementAfter, treeAfter],
1339
+ ]);
1340
+ });
1341
+
1342
+ it('updates from null to a composite child', () => {
1343
+ function Bar() {
1344
+ return null;
1345
+ }
1346
+
1347
+ function Foo({ children }) {
1348
+ return children;
1349
+ }
1350
+
1351
+ var elementBefore = <Foo>{null}</Foo>;
1352
+ var treeBefore = {
1353
+ isComposite: true,
1354
+ displayName: 'Foo',
1355
+ children: [],
1356
+ };
1357
+
1358
+ var elementAfter = <Foo><Bar /></Foo>;
1359
+ var treeAfter = {
1360
+ isComposite: true,
1361
+ displayName: 'Foo',
1362
+ children: [{
1363
+ isComposite: true,
1364
+ displayName: 'Bar',
1365
+ children: [],
1366
+ }],
1367
+ };
1368
+
1369
+ assertTreeMatches([
1370
+ [elementBefore, treeBefore],
1371
+ [elementAfter, treeAfter],
1372
+ ]);
1373
+ });
1374
+
1375
+ it('updates from a composite child to null', () => {
1376
+ function Bar() {
1377
+ return null;
1378
+ }
1379
+
1380
+ function Foo({ children }) {
1381
+ return children;
1382
+ }
1383
+
1384
+ var elementBefore = <Foo><Bar /></Foo>;
1385
+ var treeBefore = {
1386
+ isComposite: true,
1387
+ displayName: 'Foo',
1388
+ children: [{
1389
+ isComposite: true,
1390
+ displayName: 'Bar',
1391
+ children: [],
1392
+ }],
1393
+ };
1394
+
1395
+ var elementAfter = <Foo>{null}</Foo>;
1396
+ var treeAfter = {
1397
+ isComposite: true,
1398
+ displayName: 'Foo',
1399
+ children: [],
1400
+ };
1401
+
1402
+ assertTreeMatches([
1403
+ [elementBefore, treeBefore],
1404
+ [elementAfter, treeAfter],
1405
+ ]);
1406
+ });
1407
+ });
1408
+
1409
+ describe('class component', () => {
1410
+ it('updates with a native child', () => {
1411
+ var Foo = React.createClass({
1412
+ render() {
1413
+ return this.props.children;
1414
+ },
1415
+ });
1416
+
1417
+ var elementBefore = <Foo><div /></Foo>;
1418
+ var treeBefore = {
1419
+ isComposite: true,
1420
+ displayName: 'Foo',
1421
+ children: [{
1422
+ isComposite: false,
1423
+ displayName: 'div',
1424
+ children: [],
1425
+ }],
1426
+ };
1427
+
1428
+ var elementAfter = <Foo><span /></Foo>;
1429
+ var treeAfter = {
1430
+ isComposite: true,
1431
+ displayName: 'Foo',
1432
+ children: [{
1433
+ isComposite: false,
1434
+ displayName: 'span',
1435
+ children: [],
1436
+ }],
1437
+ };
1438
+
1439
+ assertTreeMatches([
1440
+ [elementBefore, treeBefore],
1441
+ [elementAfter, treeAfter],
1442
+ ]);
1443
+ });
1444
+
1445
+ it('updates from null to a native child', () => {
1446
+ var Foo = React.createClass({
1447
+ render() {
1448
+ return this.props.children;
1449
+ },
1450
+ });
1451
+
1452
+ var elementBefore = <Foo>{null}</Foo>;
1453
+ var treeBefore = {
1454
+ isComposite: true,
1455
+ displayName: 'Foo',
1456
+ children: [],
1457
+ };
1458
+
1459
+ var elementAfter = <Foo><div /></Foo>;
1460
+ var treeAfter = {
1461
+ isComposite: true,
1462
+ displayName: 'Foo',
1463
+ children: [{
1464
+ isComposite: false,
1465
+ displayName: 'div',
1466
+ children: [],
1467
+ }],
1468
+ };
1469
+
1470
+ assertTreeMatches([
1471
+ [elementBefore, treeBefore],
1472
+ [elementAfter, treeAfter],
1473
+ ]);
1474
+ });
1475
+
1476
+ it('updates from a native child to null', () => {
1477
+ var Foo = React.createClass({
1478
+ render() {
1479
+ return this.props.children;
1480
+ },
1481
+ });
1482
+
1483
+ var elementBefore = <Foo><div /></Foo>;
1484
+ var treeBefore = {
1485
+ isComposite: true,
1486
+ displayName: 'Foo',
1487
+ children: [{
1488
+ isComposite: false,
1489
+ displayName: 'div',
1490
+ children: [],
1491
+ }],
1492
+ };
1493
+
1494
+ var elementAfter = <Foo>{null}</Foo>;
1495
+ var treeAfter = {
1496
+ isComposite: true,
1497
+ displayName: 'Foo',
1498
+ children: [],
1499
+ };
1500
+
1501
+ assertTreeMatches([
1502
+ [elementBefore, treeBefore],
1503
+ [elementAfter, treeAfter],
1504
+ ]);
1505
+ });
1506
+
1507
+ it('updates from a native child to a composite child', () => {
1508
+ var Bar = React.createClass({
1509
+ render() {
1510
+ return null;
1511
+ },
1512
+ });
1513
+
1514
+ var Foo = React.createClass({
1515
+ render() {
1516
+ return this.props.children;
1517
+ },
1518
+ });
1519
+
1520
+ var elementBefore = <Foo><div /></Foo>;
1521
+ var treeBefore = {
1522
+ isComposite: true,
1523
+ displayName: 'Foo',
1524
+ children: [{
1525
+ isComposite: false,
1526
+ displayName: 'div',
1527
+ children: [],
1528
+ }],
1529
+ };
1530
+
1531
+ var elementAfter = <Foo><Bar /></Foo>;
1532
+ var treeAfter = {
1533
+ isComposite: true,
1534
+ displayName: 'Foo',
1535
+ children: [{
1536
+ isComposite: true,
1537
+ displayName: 'Bar',
1538
+ children: [],
1539
+ }],
1540
+ };
1541
+
1542
+ assertTreeMatches([
1543
+ [elementBefore, treeBefore],
1544
+ [elementAfter, treeAfter],
1545
+ ]);
1546
+ });
1547
+
1548
+ it('updates from a composite child to a native child', () => {
1549
+ var Bar = React.createClass({
1550
+ render() {
1551
+ return null;
1552
+ },
1553
+ });
1554
+
1555
+ var Foo = React.createClass({
1556
+ render() {
1557
+ return this.props.children;
1558
+ },
1559
+ });
1560
+
1561
+ var elementBefore = <Foo><Bar /></Foo>;
1562
+ var treeBefore = {
1563
+ isComposite: true,
1564
+ displayName: 'Foo',
1565
+ children: [{
1566
+ isComposite: true,
1567
+ displayName: 'Bar',
1568
+ children: [],
1569
+ }],
1570
+ };
1571
+
1572
+ var elementAfter = <Foo><div /></Foo>;
1573
+ var treeAfter = {
1574
+ isComposite: true,
1575
+ displayName: 'Foo',
1576
+ children: [{
1577
+ isComposite: false,
1578
+ displayName: 'div',
1579
+ children: [],
1580
+ }],
1581
+ };
1582
+
1583
+ assertTreeMatches([
1584
+ [elementBefore, treeBefore],
1585
+ [elementAfter, treeAfter],
1586
+ ]);
1587
+ });
1588
+
1589
+ it('updates from null to a composite child', () => {
1590
+ var Bar = React.createClass({
1591
+ render() {
1592
+ return null;
1593
+ },
1594
+ });
1595
+
1596
+ var Foo = React.createClass({
1597
+ render() {
1598
+ return this.props.children;
1599
+ },
1600
+ });
1601
+
1602
+ var elementBefore = <Foo>{null}</Foo>;
1603
+ var treeBefore = {
1604
+ isComposite: true,
1605
+ displayName: 'Foo',
1606
+ children: [],
1607
+ };
1608
+
1609
+ var elementAfter = <Foo><Bar /></Foo>;
1610
+ var treeAfter = {
1611
+ isComposite: true,
1612
+ displayName: 'Foo',
1613
+ children: [{
1614
+ isComposite: true,
1615
+ displayName: 'Bar',
1616
+ children: [],
1617
+ }],
1618
+ };
1619
+
1620
+ assertTreeMatches([
1621
+ [elementBefore, treeBefore],
1622
+ [elementAfter, treeAfter],
1623
+ ]);
1624
+ });
1625
+
1626
+ it('updates from a composite child to null', () => {
1627
+ var Bar = React.createClass({
1628
+ render() {
1629
+ return null;
1630
+ },
1631
+ });
1632
+
1633
+ var Foo = React.createClass({
1634
+ render() {
1635
+ return this.props.children;
1636
+ },
1637
+ });
1638
+
1639
+ var elementBefore = <Foo><Bar /></Foo>;
1640
+ var treeBefore = {
1641
+ isComposite: true,
1642
+ displayName: 'Foo',
1643
+ children: [{
1644
+ isComposite: true,
1645
+ displayName: 'Bar',
1646
+ children: [],
1647
+ }],
1648
+ };
1649
+
1650
+ var elementAfter = <Foo>{null}</Foo>;
1651
+ var treeAfter = {
1652
+ isComposite: true,
1653
+ displayName: 'Foo',
1654
+ children: [],
1655
+ };
1656
+
1657
+ assertTreeMatches([
1658
+ [elementBefore, treeBefore],
1659
+ [elementAfter, treeAfter],
1660
+ ]);
1661
+ });
1662
+ });
1663
});
1664
1665
it('tracks owner correctly', () => {
@@ -568,7 +1675,7 @@ describe('ReactDebugTool', () => {
1675
// Note that owner is not calculated for text nodes
1676
// because they are not created from real elements.
1677
var element = <article><Foo /></article>;
571
- var expectedTree = {
1678
+ var tree = {
1679
isComposite: false,
1680
displayName: 'article',
1681
children: [{
@@ -602,6 +1709,6 @@ describe('ReactDebugTool', () => {
1709
}],
1710
}],
1711
};
605
- assertTreeMatches(element, expectedTree, true);
1712
+ assertTreeMatches([element, tree], true);
1713
});
1714
});
src/renderers/dom/shared/ReactDOMComponent.js
+12
-1
@@ -763,7 +763,7 @@ ReactDOMComponent.Mixin = {
763
ReactInstrumentation.debugTool.onSetIsComposite(inlinedTextDebugID, false);
764
ReactInstrumentation.debugTool.onSetDisplayName(inlinedTextDebugID, '#text');
765
ReactInstrumentation.debugTool.onSetChildren(inlinedTextDebugID, []);
766
- ReactInstrumentation.debugTool.onSetText(inlinedTextDebugID, contentToUse);
766
+ ReactInstrumentation.debugTool.onSetText(inlinedTextDebugID, '' + contentToUse);
767
}
768
DOMLazyTree.queueText(lazyTree, contentToUse);
769
} else if (childrenToUse != null) {
@@ -1012,11 +1012,22 @@ ReactDOMComponent.Mixin = {
1012
this.updateChildren(null, transaction, context);
1013
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
1014
this.updateTextContent('');
1015
+ if (__DEV__) {
1016
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
1017
+ }
1018
}
1019
1020
if (nextContent != null) {
1021
if (lastContent !== nextContent) {
1022
this.updateTextContent('' + nextContent);
1023
+ if (__DEV__) {
1024
+ var inlinedTextDebugID = this._debugID + '#text';
1025
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, [inlinedTextDebugID]);
1026
+ ReactInstrumentation.debugTool.onSetIsComposite(inlinedTextDebugID, false);
1027
+ ReactInstrumentation.debugTool.onSetDisplayName(inlinedTextDebugID, '#text');
1028
+ ReactInstrumentation.debugTool.onSetChildren(inlinedTextDebugID, []);
1029
+ ReactInstrumentation.debugTool.onSetText(inlinedTextDebugID, '' + nextContent);
1030
+ }
1031
}
1032
} else if (nextHtml != null) {
1033
if (lastHtml !== nextHtml) {
src/renderers/dom/shared/ReactDOMTextComponent.js
+7
@@ -14,6 +14,7 @@
14
var DOMChildrenOperations = require('DOMChildrenOperations');
15
var DOMLazyTree = require('DOMLazyTree');
16
var ReactDOMComponentTree = require('ReactDOMComponentTree');
17
+var ReactInstrumentation = require('ReactInstrumentation');
18
var ReactPerf = require('ReactPerf');
19
20
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
@@ -67,6 +68,8 @@ Object.assign(ReactDOMTextComponent.prototype, {
68
context
69
) {
70
if (__DEV__) {
71
+ ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
72
+
73
var parentInfo;
74
if (nativeParent != null) {
75
parentInfo = nativeParent._ancestorInfo;
@@ -140,6 +143,10 @@ Object.assign(ReactDOMTextComponent.prototype, {
143
commentNodes[1],
144
nextStringText
145
);
146
+
147
+ if (__DEV__) {
148
+ ReactInstrumentation.debugTool.onSetText(this._debugID, nextStringText);
149
+ }
150
}
151
}
152
},
src/renderers/shared/reconciler/ReactCompositeComponent.js
+14
-5
@@ -378,12 +378,12 @@ var ReactCompositeComponentMixin = {
378
renderedElement
379
);
380
if (__DEV__) {
381
- if (this._renderedComponent._debugID) {
382
- ReactInstrumentation.debugTool.onSetChildren(
383
- this._debugID,
381
+ ReactInstrumentation.debugTool.onSetChildren(
382
+ this._debugID,
383
+ this._renderedNodeType === ReactNodeTypes.EMPTY ?
384
+ [] :
385
[this._renderedComponent._debugID]
385
- );
386
- }
386
+ );
387
}
388
389
var markup = ReactReconciler.mountComponent(
@@ -861,6 +861,15 @@ var ReactCompositeComponentMixin = {
861
this._renderedComponent = this._instantiateReactComponent(
862
nextRenderedElement
863
);
864
+ if (__DEV__) {
865
+ ReactInstrumentation.debugTool.onSetChildren(
866
+ this._debugID,
867
+ this._renderedNodeType === ReactNodeTypes.EMPTY ?
868
+ [] :
869
+ [this._renderedComponent._debugID]
870
+ );
871
+ }
872
+
873
var nextMarkup = ReactReconciler.mountComponent(
874
this._renderedComponent,
875
transaction,
src/renderers/shared/reconciler/ReactMultiChild.js
+19
-7
@@ -216,13 +216,6 @@ var ReactMultiChild = {
216
);
217
this._renderedChildren = children;
218
219
- if (__DEV__) {
220
- ReactInstrumentation.debugTool.onSetChildren(
221
- this._debugID,
222
- Object.keys(children).map(key => children[key]._debugID)
223
- );
224
- }
225
-
219
var mountImages = [];
220
var index = 0;
221
for (var name in children) {
@@ -239,6 +232,16 @@ var ReactMultiChild = {
232
mountImages.push(mountImage);
233
}
234
}
235
+
236
+ if (__DEV__) {
237
+ ReactInstrumentation.debugTool.onSetChildren(
238
+ this._debugID,
239
+ children ?
240
+ Object.keys(children).map(key => children[key]._debugID) :
241
+ []
242
+ );
243
+ }
244
+
245
return mountImages;
246
},
247
@@ -366,6 +369,15 @@ var ReactMultiChild = {
369
processQueue(this, updates);
370
}
371
this._renderedChildren = nextChildren;
372
+
373
+ if (__DEV__) {
374
+ ReactInstrumentation.debugTool.onSetChildren(
375
+ this._debugID,
376
+ nextChildren ?
377
+ Object.keys(nextChildren).map(key => nextChildren[key]._debugID) :
378
+ []
379
+ );
380
+ }
381
},
382
383
/**
src/renderers/shared/reconciler/instantiateReactComponent.js
+12
-17
@@ -42,18 +42,18 @@ function getDeclarationErrorAddendum(owner) {
42
}
43
44
function getDisplayName(instance) {
45
- var element = instance._currentElement;
46
- if (element == null) {
47
- return '#empty';
48
- } else if (typeof element === 'string' || typeof element === 'number') {
49
- return '#text';
50
- } else if (typeof element.type === 'string') {
51
- return element.type;
52
- } else if (instance.getName) {
53
- return instance.getName() || 'Unknown';
54
- } else {
55
- return element.type.displayName || element.type.name || 'Unknown';
56
- }
45
+ var element = instance._currentElement;
46
+ if (element == null) {
47
+ return '#empty';
48
+ } else if (typeof element === 'string' || typeof element === 'number') {
49
+ return '#text';
50
+ } else if (typeof element.type === 'string') {
51
+ return element.type;
52
+ } else if (instance.getName) {
53
+ return instance.getName() || 'Unknown';
54
+ } else {
55
+ return element.type.displayName || element.type.name || 'Unknown';
56
+ }
57
}
58
59
/**
@@ -85,7 +85,6 @@ function instantiateReactComponent(node) {
85
var instance;
86
87
var isEmpty = false;
88
- var isText = false;
88
var isComposite = false;
89
90
if (node === null || node === false) {
@@ -115,7 +114,6 @@ function instantiateReactComponent(node) {
114
instance = new ReactCompositeComponentWrapper(element);
115
}
116
} else if (typeof node === 'string' || typeof node === 'number') {
118
- isText = true;
117
instance = ReactNativeComponent.createInstanceForText(node);
118
} else {
119
invariant(
@@ -156,9 +154,6 @@ function instantiateReactComponent(node) {
154
if (owner) {
155
ReactInstrumentation.debugTool.onSetOwner(instance._debugID, owner._debugID);
156
}
159
- if (isText) {
160
- ReactInstrumentation.debugTool.onSetText(instance._debugID, node);
161
- }
157
}
158
159
// Internal instances should fully constructed at this point, so they should