298
`);
299
});
300
});
301
+
302
+ describe('components behind filtered fibers should not report false re-renders', () => {
303
+ it('should not report a component as re-rendered when its filtered parent bailed out', () => {
304
+ let triggerUpdate;
305
+
306
+ function Count() {
307
+ const [count, setCount] = React.useState(0);
308
+ triggerUpdate = () => setCount(c => c + 1);
309
+ Scheduler.unstable_advanceTime(5);
310
+ return count;
311
+ }
312
+
313
+ function Greeting() {
314
+ Scheduler.unstable_advanceTime(3);
315
+ return 'Hello';
316
+ }
317
+
318
+ function App() {
319
+ Scheduler.unstable_advanceTime(1);
320
+ return (
321
+ <React.Fragment>
322
+ <Count />
323
+ <div>
324
+ <Greeting />
325
+ </div>
326
+ </React.Fragment>
327
+ );
328
+ }
329
+
330
+ utils.act(() => store.profilerStore.startProfiling());
331
+ utils.act(() => render(<App />));
332
+
333
+ // Verify tree structure: div is filtered, so Greeting appears as child of App
334
+ expect(store).toMatchInlineSnapshot(`
335
+ [root]
336
+ ▾ <App>
337
+ <Count>
338
+ <Greeting>
339
+ `);
340
+
341
+ // Trigger a state update in Count. Should not cause Greeting to re-render.
342
+ utils.act(() => triggerUpdate());
343
+
344
+ utils.act(() => store.profilerStore.stopProfiling());
345
+
346
+ const rootID = store.roots[0];
347
+ const {chartData} = getFlamegraphChartData(rootID, 1);
348
+ const allNodes = chartData.rows.flat();
349
+
350
+ expect(allNodes).toEqual([
351
+ expect.objectContaining({name: 'App', didRender: false}),
352
+ expect.objectContaining({name: 'Greeting', didRender: false}),
353
+ expect.objectContaining({name: 'Count', didRender: true}),
354
+ ]);
355
+ });
356
+
357
+ it('should not report a component as re-rendered when behind a filtered fragment', () => {
358
+ let triggerUpdate;
359
+
360
+ function Count() {
361
+ const [count, setCount] = React.useState(0);
362
+ triggerUpdate = () => setCount(c => c + 1);
363
+ Scheduler.unstable_advanceTime(5);
364
+ return count;
365
+ }
366
+
367
+ function Greeting() {
368
+ Scheduler.unstable_advanceTime(3);
369
+ return 'Hello';
370
+ }
371
+
372
+ function App() {
373
+ Scheduler.unstable_advanceTime(1);
374
+ return (
375
+ <React.Fragment>
376
+ <Count />
377
+ <React.Fragment>
378
+ <Greeting />
379
+ </React.Fragment>
380
+ </React.Fragment>
381
+ );
382
+ }
383
+
384
+ utils.act(() => store.profilerStore.startProfiling());
385
+ utils.act(() => render(<App />));
386
+
387
+ // Fragment with null key is filtered, so Greeting appears as child of App
388
+ expect(store).toMatchInlineSnapshot(`
389
+ [root]
390
+ ▾ <App>
391
+ <Count>
392
+ <Greeting>
393
+ `);
394
+
395
+ // Trigger a state update in Count
396
+ utils.act(() => triggerUpdate());
397
+
398
+ utils.act(() => store.profilerStore.stopProfiling());
399
+
400
+ const rootID = store.roots[0];
401
+ const {chartData} = getFlamegraphChartData(rootID, 1);
402
+ const allNodes = chartData.rows.flat();
403
+
404
+ expect(allNodes).toEqual([
405
+ expect.objectContaining({name: 'App', didRender: false}),
406
+ expect.objectContaining({name: 'Greeting', didRender: false}),
407
+ expect.objectContaining({name: 'Count', didRender: true}),
408
+ ]);
409
+ });
410
+
411
+ it('should correctly report sibling components that did not re-render', () => {
412
+ let triggerUpdate;
413
+
414
+ function Count() {
415
+ const [count, setCount] = React.useState(0);
416
+ triggerUpdate = () => setCount(c => c + 1);
417
+ Scheduler.unstable_advanceTime(5);
418
+ return count;
419
+ }
420
+
421
+ function Greeting() {
422
+ Scheduler.unstable_advanceTime(3);
423
+ return 'Hello';
424
+ }
425
+
426
+ function App() {
427
+ Scheduler.unstable_advanceTime(1);
428
+ return (
429
+ <React.Fragment>
430
+ <Count />
431
+ <Greeting />
432
+ </React.Fragment>
433
+ );
434
+ }
435
+
436
+ utils.act(() => store.profilerStore.startProfiling());
437
+ utils.act(() => render(<App />));
438
+
439
+ expect(store).toMatchInlineSnapshot(`
440
+ [root]
441
+ ▾ <App>
442
+ <Count>
443
+ <Greeting>
444
+ `);
445
+
446
+ utils.act(() => triggerUpdate());
447
+
448
+ utils.act(() => store.profilerStore.stopProfiling());
449
+
450
+ const rootID = store.roots[0];
451
+ const {chartData} = getFlamegraphChartData(rootID, 1);
452
+ const allNodes = chartData.rows.flat();
453
+
454
+ expect(allNodes).toEqual([
455
+ expect.objectContaining({name: 'App', didRender: false}),
456
+ expect.objectContaining({name: 'Greeting', didRender: false}),
457
+ expect.objectContaining({name: 'Count', didRender: true}),
458
+ ]);
459
+ });
460
+ });
461
});