@samitouri / QOS-React-2 / commits / bfb40225b5

Scheduling Profiler does not warn about long transitions (#22614)

Brian Vaughn committed Oct 21, 2021 at 15:16 UTC bfb40225b509dc65314f3fda95633090bf29ebf0
2 files changed +147 -1
packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js
+137
@@ -1601,6 +1601,143 @@ describe('preprocessData', () => {
1601 );
1602 }
1603 });
1604 +
1605 + it('should not warn about transition updates scheduled during commit phase', async () => {
1606 + function Component() {
1607 + const [value, setValue] = React.useState(0);
1608 + // eslint-disable-next-line no-unused-vars
1609 + const [isPending, startTransition] = React.useTransition();
1610 +
1611 + Scheduler.unstable_yieldValue(
1612 + `Component rendered with value ${value}`,
1613 + );
1614 +
1615 + // Fake a long render
1616 + if (value !== 0) {
1617 + Scheduler.unstable_yieldValue('Long render');
1618 + startTime += 20000;
1619 + }
1620 +
1621 + React.useLayoutEffect(() => {
1622 + startTransition(() => {
1623 + setValue(1);
1624 + });
1625 + }, []);
1626 +
1627 + return value;
1628 + }
1629 +
1630 + if (gate(flags => flags.enableSchedulingProfiler)) {
1631 + const cpuProfilerSample = creactCpuProfilerSample();
1632 +
1633 + const root = ReactDOM.createRoot(document.createElement('div'));
1634 + act(() => {
1635 + root.render(<Component />);
1636 + });
1637 +
1638 + expect(Scheduler).toHaveYielded([
1639 + 'Component rendered with value 0',
1640 + 'Component rendered with value 0',
1641 + 'Component rendered with value 1',
1642 + 'Long render',
1643 + ]);
1644 +
1645 + const testMarks = [];
1646 + clearedMarks.forEach(markName => {
1647 + if (markName === '--component-render-start-Component') {
1648 + // Fake a long running render
1649 + startTime += 20000;
1650 + }
1651 +
1652 + testMarks.push({
1653 + pid: ++pid,
1654 + tid: ++tid,
1655 + ts: ++startTime,
1656 + args: {data: {}},
1657 + cat: 'blink.user_timing',
1658 + name: markName,
1659 + ph: 'R',
1660 + });
1661 + });
1662 +
1663 + const data = await preprocessData([
1664 + cpuProfilerSample,
1665 + ...createBoilerplateEntries(),
1666 + ...testMarks,
1667 + ]);
1668 +
1669 + data.schedulingEvents.forEach(event => {
1670 + expect(event.warning).toBeNull();
1671 + });
1672 + }
1673 + });
1674 +
1675 + it('should not warn about deferred value updates scheduled during commit phase', async () => {
1676 + function Component() {
1677 + const [value, setValue] = React.useState(0);
1678 + const deferredValue = React.useDeferredValue(value);
1679 +
1680 + Scheduler.unstable_yieldValue(
1681 + `Component rendered with value ${value} and deferredValue ${deferredValue}`,
1682 + );
1683 +
1684 + // Fake a long render
1685 + if (deferredValue !== 0) {
1686 + Scheduler.unstable_yieldValue('Long render');
1687 + startTime += 20000;
1688 + }
1689 +
1690 + React.useLayoutEffect(() => {
1691 + setValue(1);
1692 + }, []);
1693 +
1694 + return value + deferredValue;
1695 + }
1696 +
1697 + if (gate(flags => flags.enableSchedulingProfiler)) {
1698 + const cpuProfilerSample = creactCpuProfilerSample();
1699 +
1700 + const root = ReactDOM.createRoot(document.createElement('div'));
1701 + act(() => {
1702 + root.render(<Component />);
1703 + });
1704 +
1705 + expect(Scheduler).toHaveYielded([
1706 + 'Component rendered with value 0 and deferredValue 0',
1707 + 'Component rendered with value 1 and deferredValue 0',
1708 + 'Component rendered with value 1 and deferredValue 1',
1709 + 'Long render',
1710 + ]);
1711 +
1712 + const testMarks = [];
1713 + clearedMarks.forEach(markName => {
1714 + if (markName === '--component-render-start-Component') {
1715 + // Fake a long running render
1716 + startTime += 20000;
1717 + }
1718 +
1719 + testMarks.push({
1720 + pid: ++pid,
1721 + tid: ++tid,
1722 + ts: ++startTime,
1723 + args: {data: {}},
1724 + cat: 'blink.user_timing',
1725 + name: markName,
1726 + ph: 'R',
1727 + });
1728 + });
1729 +
1730 + const data = await preprocessData([
1731 + cpuProfilerSample,
1732 + ...createBoilerplateEntries(),
1733 + ...testMarks,
1734 + ]);
1735 +
1736 + data.schedulingEvents.forEach(event => {
1737 + expect(event.warning).toBeNull();
1738 + });
1739 + }
1740 + });
1741 });
1742
1743 describe('errors thrown while rendering', () => {
packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js
+10 -1
@@ -1160,7 +1160,16 @@ export default async function preprocessData(
1160 // See how long the subsequent batch of React work was.
1161 const [startTime, stopTime] = getBatchRange(batchUID, profilerData);
1162 if (stopTime - startTime > NESTED_UPDATE_DURATION_THRESHOLD) {
1163 - schedulingEvent.warning = WARNING_STRINGS.NESTED_UPDATE;
1163 + // Don't warn about transition updates scheduled during the commit phase.
1164 + // e.g. useTransition, useDeferredValue
1165 + // These are allowed to be long-running.
1166 + if (
1167 + !schedulingEvent.lanes.some(
1168 + lane => profilerData.laneToLabelMap.get(lane) === 'Transition',
1169 + )
1170 + ) {
1171 + schedulingEvent.warning = WARNING_STRINGS.NESTED_UPDATE;
1172 + }
1173 }
1174 });
1175 state.potentialSuspenseEventsOutsideOfTransition.forEach(