Skip to content

Commit bfb4022

Browse files
author
Brian Vaughn
authored
Scheduling Profiler does not warn about long transitions (#22614)
1 parent 0e8a5af commit bfb4022

File tree

2 files changed

+147
-1
lines changed

2 files changed

+147
-1
lines changed

packages/react-devtools-scheduling-profiler/src/import-worker/__tests__/preprocessData-test.internal.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,143 @@ describe('preprocessData', () => {
16011601
);
16021602
}
16031603
});
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+
});
16041741
});
16051742

16061743
describe('errors thrown while rendering', () => {

packages/react-devtools-scheduling-profiler/src/import-worker/preprocessData.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,16 @@ export default async function preprocessData(
11601160
// See how long the subsequent batch of React work was.
11611161
const [startTime, stopTime] = getBatchRange(batchUID, profilerData);
11621162
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+
}
11641173
}
11651174
});
11661175
state.potentialSuspenseEventsOutsideOfTransition.forEach(

0 commit comments

Comments
 (0)