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(