@samitouri / QOS-React-2 / commits / 6da04b5d88

Fix interaction tracing for batched update mounts (#15567)

* Added failing test for act+interaction tracing * Mark pending interactions on root for legacy unbatched phase

Brian Vaughn committed May 6, 2019 at 12:59 UTC 6da04b5d886b272e241178694e15ced22c5a2c05
4 files changed +164 -1
package.json
+1 -1
@@ -87,7 +87,7 @@
87 "@mattiasbuelens/web-streams-polyfill": "0.1.0"
88 },
89 "devEngines": {
90 - "node": "8.x || 9.x || 10.x || 11.x"
90 + "node": "8.x || 9.x || 10.x || 11.x || 12.x"
91 },
92 "jest": {
93 "testRegex": "/scripts/jest/dont-run-jest-directly\\.js$"
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
+80
@@ -10,6 +10,7 @@
10 let React;
11 let ReactDOM;
12 let ReactTestUtils;
13 +let SchedulerTracing;
14 let act;
15 let container;
16
@@ -29,6 +30,7 @@ describe('ReactTestUtils.act()', () => {
30 React = require('react');
31 ReactDOM = require('react-dom');
32 ReactTestUtils = require('react-dom/test-utils');
33 + SchedulerTracing = require('scheduler/tracing');
34 act = ReactTestUtils.act;
35 container = document.createElement('div');
36 document.body.appendChild(container);
@@ -423,4 +425,82 @@ describe('ReactTestUtils.act()', () => {
425 expect(el.innerHTML).toBe('5');
426 });
427 });
428 +
429 + describe('interaction tracing', () => {
430 + if (__DEV__) {
431 + it('should correctly trace interactions for sync roots', () => {
432 + let expectedInteraction;
433 +
434 + const Component = jest.fn(() => {
435 + expect(expectedInteraction).toBeDefined();
436 +
437 + const interactions = SchedulerTracing.unstable_getCurrent();
438 + expect(interactions.size).toBe(1);
439 + expect(interactions).toContain(expectedInteraction);
440 +
441 + return null;
442 + });
443 +
444 + act(() => {
445 + SchedulerTracing.unstable_trace(
446 + 'mount traced inside act',
447 + performance.now(),
448 + () => {
449 + const interactions = SchedulerTracing.unstable_getCurrent();
450 + expect(interactions.size).toBe(1);
451 + expectedInteraction = Array.from(interactions)[0];
452 +
453 + ReactDOM.render(<Component />, container);
454 + },
455 + );
456 + });
457 +
458 + act(() => {
459 + SchedulerTracing.unstable_trace(
460 + 'update traced inside act',
461 + performance.now(),
462 + () => {
463 + const interactions = SchedulerTracing.unstable_getCurrent();
464 + expect(interactions.size).toBe(1);
465 + expectedInteraction = Array.from(interactions)[0];
466 +
467 + ReactDOM.render(<Component />, container);
468 + },
469 + );
470 + });
471 +
472 + const secondContainer = document.createElement('div');
473 +
474 + SchedulerTracing.unstable_trace(
475 + 'mount traced outside act',
476 + performance.now(),
477 + () => {
478 + act(() => {
479 + const interactions = SchedulerTracing.unstable_getCurrent();
480 + expect(interactions.size).toBe(1);
481 + expectedInteraction = Array.from(interactions)[0];
482 +
483 + ReactDOM.render(<Component />, secondContainer);
484 + });
485 + },
486 + );
487 +
488 + SchedulerTracing.unstable_trace(
489 + 'update traced outside act',
490 + performance.now(),
491 + () => {
492 + act(() => {
493 + const interactions = SchedulerTracing.unstable_getCurrent();
494 + expect(interactions.size).toBe(1);
495 + expectedInteraction = Array.from(interactions)[0];
496 +
497 + ReactDOM.render(<Component />, secondContainer);
498 + });
499 + },
500 + );
501 +
502 + expect(Component).toHaveBeenCalledTimes(4);
503 + });
504 + }
505 + });
506 });
packages/react-dom/src/__tests__/ReactUpdates-test.js
+80
@@ -1618,4 +1618,84 @@ describe('ReactUpdates', () => {
1618 expect(container.textContent).toBe('1000');
1619 });
1620 }
1621 +
1622 + if (__DEV__) {
1623 + it('should properly trace interactions within batched udpates', () => {
1624 + const SchedulerTracing = require('scheduler/tracing');
1625 +
1626 + let expectedInteraction;
1627 +
1628 + const container = document.createElement('div');
1629 +
1630 + const Component = jest.fn(() => {
1631 + expect(expectedInteraction).toBeDefined();
1632 +
1633 + const interactions = SchedulerTracing.unstable_getCurrent();
1634 + expect(interactions.size).toBe(1);
1635 + expect(interactions).toContain(expectedInteraction);
1636 +
1637 + return null;
1638 + });
1639 +
1640 + ReactDOM.unstable_batchedUpdates(() => {
1641 + SchedulerTracing.unstable_trace(
1642 + 'mount traced inside a batched update',
1643 + 1,
1644 + () => {
1645 + const interactions = SchedulerTracing.unstable_getCurrent();
1646 + expect(interactions.size).toBe(1);
1647 + expectedInteraction = Array.from(interactions)[0];
1648 +
1649 + ReactDOM.render(<Component />, container);
1650 + },
1651 + );
1652 + });
1653 +
1654 + ReactDOM.unstable_batchedUpdates(() => {
1655 + SchedulerTracing.unstable_trace(
1656 + 'update traced inside a batched update',
1657 + 2,
1658 + () => {
1659 + const interactions = SchedulerTracing.unstable_getCurrent();
1660 + expect(interactions.size).toBe(1);
1661 + expectedInteraction = Array.from(interactions)[0];
1662 +
1663 + ReactDOM.render(<Component />, container);
1664 + },
1665 + );
1666 + });
1667 +
1668 + const secondContainer = document.createElement('div');
1669 +
1670 + SchedulerTracing.unstable_trace(
1671 + 'mount traced outside a batched update',
1672 + 3,
1673 + () => {
1674 + ReactDOM.unstable_batchedUpdates(() => {
1675 + const interactions = SchedulerTracing.unstable_getCurrent();
1676 + expect(interactions.size).toBe(1);
1677 + expectedInteraction = Array.from(interactions)[0];
1678 +
1679 + ReactDOM.render(<Component />, secondContainer);
1680 + });
1681 + },
1682 + );
1683 +
1684 + SchedulerTracing.unstable_trace(
1685 + 'update traced outside a batched update',
1686 + 4,
1687 + () => {
1688 + ReactDOM.unstable_batchedUpdates(() => {
1689 + const interactions = SchedulerTracing.unstable_getCurrent();
1690 + expect(interactions.size).toBe(1);
1691 + expectedInteraction = Array.from(interactions)[0];
1692 +
1693 + ReactDOM.render(<Component />, container);
1694 + });
1695 + },
1696 + );
1697 +
1698 + expect(Component).toHaveBeenCalledTimes(4);
1699 + });
1700 + }
1701 });
packages/react-reconciler/src/ReactFiberScheduler.js
+3
@@ -331,6 +331,9 @@ export function scheduleUpdateOnFiber(
331
332 if (expirationTime === Sync) {
333 if (workPhase === LegacyUnbatchedPhase) {
334 + // Register pending interactions on the root to avoid losing traced interaction data.
335 + schedulePendingInteraction(root, expirationTime);
336 +
337 // This is a legacy edge case. The initial mount of a ReactDOM.render-ed
338 // root inside of batchedUpdates should be synchronous, but layout updates
339 // should be deferred until the end of the batch.