mirror of
https://github.com/facebook/react.git
synced 2026-02-25 23:15:04 +00:00
Interaction chart with commit blocks rendering
This commit is contained in:
@@ -892,7 +892,10 @@ export function attach(
|
||||
actualDurations: [],
|
||||
commitTime: performance.now() - profilingStartTime,
|
||||
interactions: Array.from(root.memoizedInteractions).map(
|
||||
(interaction: Interaction) => ({ ...interaction })
|
||||
(interaction: Interaction) => ({
|
||||
...interaction,
|
||||
timestamp: interaction.timestamp - profilingStartTime,
|
||||
})
|
||||
),
|
||||
maxActualDuration: 0,
|
||||
};
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
getChartData as getFlamegraphChartData,
|
||||
invalidateChartData as invalidateFlamegraphChartData,
|
||||
} from 'src/devtools/views/Profiler/FlamegraphChartBuilder';
|
||||
import {
|
||||
getChartData as getInteractionsChartData,
|
||||
invalidateChartData as invalidateInteractionsChartData,
|
||||
} from 'src/devtools/views/Profiler/InteractionsChartBuilder';
|
||||
import {
|
||||
getChartData as getRankedChartData,
|
||||
invalidateChartData as invalidateRankedChartData,
|
||||
@@ -25,10 +29,12 @@ import type {
|
||||
import type {
|
||||
CommitDetails as CommitDetailsFrontend,
|
||||
Interactions as InteractionsFrontend,
|
||||
InteractionWithCommits,
|
||||
CommitTree as CommitTreeFrontend,
|
||||
ProfilingSummary as ProfilingSummaryFrontend,
|
||||
} from 'src/devtools/views/Profiler/types';
|
||||
import type { ChartData as FlamegraphChartData } from 'src/devtools/views/Profiler/FlamegraphChartBuilder';
|
||||
import type { ChartData as InteractionsChartData } from 'src/devtools/views/Profiler/InteractionsChartBuilder';
|
||||
import type { ChartData as RankedChartData } from 'src/devtools/views/Profiler/RankedChartBuilder';
|
||||
|
||||
type CommitDetailsParams = {|
|
||||
@@ -110,10 +116,7 @@ export default class ProfilingCache {
|
||||
return new Promise(resolve => {
|
||||
if (!this._store.profilingOperations.has(rootID)) {
|
||||
// If no profiling data was recorded for this root, skip the round trip.
|
||||
resolve({
|
||||
interactions: [],
|
||||
rootID,
|
||||
});
|
||||
resolve([]);
|
||||
} else {
|
||||
this._pendingInteractionsMap.set(rootID, resolve);
|
||||
this._bridge.send('getInteractions', {
|
||||
@@ -190,6 +193,21 @@ export default class ProfilingCache {
|
||||
rootID,
|
||||
});
|
||||
|
||||
getInteractionsChartData = ({
|
||||
interactions,
|
||||
profilingSummary,
|
||||
rootID,
|
||||
}: {|
|
||||
interactions: Array<InteractionWithCommits>,
|
||||
profilingSummary: ProfilingSummaryFrontend,
|
||||
rootID: number,
|
||||
|}): InteractionsChartData =>
|
||||
getInteractionsChartData({
|
||||
interactions,
|
||||
profilingSummary,
|
||||
rootID,
|
||||
});
|
||||
|
||||
getRankedChartData = ({
|
||||
commitDetails,
|
||||
commitIndex,
|
||||
@@ -215,6 +233,7 @@ export default class ProfilingCache {
|
||||
// Invalidate non-Suspense caches too.
|
||||
invalidateCommitTrees();
|
||||
invalidateFlamegraphChartData();
|
||||
invalidateInteractionsChartData();
|
||||
invalidateRankedChartData();
|
||||
|
||||
this._pendingCommitDetailsMap.clear();
|
||||
@@ -249,10 +268,7 @@ export default class ProfilingCache {
|
||||
if (resolve != null) {
|
||||
this._pendingInteractionsMap.delete(rootID);
|
||||
|
||||
resolve({
|
||||
interactions,
|
||||
rootID,
|
||||
});
|
||||
resolve(interactions);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,31 @@
|
||||
}
|
||||
|
||||
.SelectedInteraction {
|
||||
background-color: var(--color-selected-background);
|
||||
color: var(--color-selected-foreground);
|
||||
background-color: var(--color-hover-background);
|
||||
}
|
||||
|
||||
.Name {
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.Timeline {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.InteractionLine {
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
background-color: var(--color-commit-did-not-render);
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
|
||||
.CommitBox {
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: var(--color-commit-did-not-render);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { areEqual } from 'react-window';
|
||||
import { getGradientColor } from './utils';
|
||||
|
||||
import styles from './InteractionListItem.css';
|
||||
|
||||
@@ -14,7 +15,18 @@ type Props = {
|
||||
};
|
||||
|
||||
function InteractionListItem({ data: itemData, index, style }: Props) {
|
||||
const { interactions, selectedInteractionID, selectInteraction } = itemData;
|
||||
const {
|
||||
chartData,
|
||||
interactions,
|
||||
labelWidth,
|
||||
profilingSummary,
|
||||
scaleX,
|
||||
selectedInteractionID,
|
||||
selectInteraction,
|
||||
} = itemData;
|
||||
|
||||
const { maxCommitDuration } = chartData;
|
||||
const { commitDurations, commitTimes } = profilingSummary;
|
||||
|
||||
const interaction = interactions[index];
|
||||
|
||||
@@ -22,7 +34,11 @@ function InteractionListItem({ data: itemData, index, style }: Props) {
|
||||
selectInteraction(interaction.id);
|
||||
}, [interaction, selectInteraction]);
|
||||
|
||||
// TODO (profiling Render commit bar)
|
||||
const startTime = interaction.timestamp;
|
||||
const stopTime =
|
||||
interaction.commits.length > 0
|
||||
? commitTimes[interaction.commits[interaction.commits.length - 1]]
|
||||
: interaction.timestamp;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -33,9 +49,36 @@ function InteractionListItem({ data: itemData, index, style }: Props) {
|
||||
}
|
||||
onClick={handleClick}
|
||||
style={style}
|
||||
title={interaction.name}
|
||||
>
|
||||
{interaction.name}
|
||||
<div
|
||||
className={styles.Name}
|
||||
style={{ maxWidth: labelWidth }}
|
||||
title={interaction.name}
|
||||
>
|
||||
{interaction.name}
|
||||
</div>
|
||||
<div
|
||||
className={styles.InteractionLine}
|
||||
style={{
|
||||
left: labelWidth + scaleX(startTime, 0),
|
||||
width: scaleX(stopTime - startTime, 0),
|
||||
}}
|
||||
/>
|
||||
{interaction.commits.map(commitIndex => (
|
||||
<div
|
||||
className={styles.CommitBox}
|
||||
key={commitIndex}
|
||||
style={{
|
||||
backgroundColor: getGradientColor(
|
||||
Math.min(
|
||||
1,
|
||||
Math.max(0, commitDurations[commitIndex] / maxCommitDuration)
|
||||
) || 0
|
||||
),
|
||||
left: labelWidth + scaleX(commitTimes[commitIndex], 0),
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,19 @@ import { ProfilerContext } from './ProfilerContext';
|
||||
import InteractionListItem from './InteractionListItem';
|
||||
import NoInteractions from './NoInteractions';
|
||||
import { StoreContext } from '../context';
|
||||
import { scale } from './utils';
|
||||
|
||||
import styles from './Interactions.css';
|
||||
|
||||
import type { InteractionWithCommits } from './types';
|
||||
import type { ChartData } from './InteractionsChartBuilder';
|
||||
import type { InteractionWithCommits, ProfilingSummary } from './types';
|
||||
|
||||
export type ItemData = {|
|
||||
chartData: ChartData,
|
||||
interactions: Array<InteractionWithCommits>,
|
||||
labelWidth: number,
|
||||
profilingSummary: ProfilingSummary,
|
||||
scaleX: (value: number, fallbackValue: number) => number,
|
||||
selectedInteractionID: number | null,
|
||||
selectInteraction: (id: number | null) => void,
|
||||
|};
|
||||
@@ -37,19 +43,44 @@ function Interactions({ height, width }: {| height: number, width: number |}) {
|
||||
} = useContext(ProfilerContext);
|
||||
const { profilingCache } = useContext(StoreContext);
|
||||
|
||||
const { interactions } = profilingCache.Interactions.read({
|
||||
const interactions = profilingCache.Interactions.read({
|
||||
rendererID: ((rendererID: any): number),
|
||||
rootID: ((rootID: any): number),
|
||||
});
|
||||
|
||||
const itemData = useMemo<ItemData>(
|
||||
() => ({
|
||||
const profilingSummary = profilingCache.ProfilingSummary.read({
|
||||
rendererID: ((rendererID: any): number),
|
||||
rootID: ((rootID: any): number),
|
||||
});
|
||||
|
||||
const chartData = profilingCache.getInteractionsChartData({
|
||||
interactions,
|
||||
profilingSummary,
|
||||
rootID: ((rootID: any): number),
|
||||
});
|
||||
|
||||
const itemData = useMemo<ItemData>(() => {
|
||||
// TODO (profiling) constants
|
||||
const labelWidth = Math.min(200, width / 5);
|
||||
const timelineWidth = width - labelWidth - 10;
|
||||
|
||||
return {
|
||||
chartData,
|
||||
interactions,
|
||||
labelWidth,
|
||||
profilingSummary,
|
||||
scaleX: scale(0, chartData.lastInteractionTime, 0, timelineWidth),
|
||||
selectedInteractionID,
|
||||
selectInteraction,
|
||||
}),
|
||||
[interactions, selectedInteractionID, selectInteraction]
|
||||
);
|
||||
};
|
||||
}, [
|
||||
chartData,
|
||||
interactions,
|
||||
profilingSummary,
|
||||
selectedInteractionID,
|
||||
selectInteraction,
|
||||
width,
|
||||
]);
|
||||
|
||||
// TODO (profiling) Up/down arrow keys to select prev/next interaction.
|
||||
|
||||
|
||||
45
src/devtools/views/Profiler/InteractionsChartBuilder.js
Normal file
45
src/devtools/views/Profiler/InteractionsChartBuilder.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// @flow
|
||||
|
||||
import type { InteractionWithCommits, ProfilingSummary } from './types';
|
||||
|
||||
export type ChartData = {|
|
||||
lastInteractionTime: number,
|
||||
maxCommitDuration: number,
|
||||
|};
|
||||
|
||||
const cachedChartData: Map<number, ChartData> = new Map();
|
||||
|
||||
export function getChartData({
|
||||
interactions,
|
||||
profilingSummary,
|
||||
rootID,
|
||||
}: {|
|
||||
interactions: Array<InteractionWithCommits>,
|
||||
profilingSummary: ProfilingSummary,
|
||||
rootID: number,
|
||||
|}): ChartData {
|
||||
if (cachedChartData.has(rootID)) {
|
||||
return ((cachedChartData.get(rootID): any): ChartData);
|
||||
}
|
||||
|
||||
const { commitDurations, commitTimes } = profilingSummary;
|
||||
|
||||
const lastInteractionTime =
|
||||
commitTimes.length > 0 ? commitTimes[commitTimes.length - 1] : 0;
|
||||
|
||||
let maxCommitDuration = 0;
|
||||
|
||||
commitDurations.forEach(commitDuration => {
|
||||
maxCommitDuration = Math.max(maxCommitDuration, commitDuration);
|
||||
});
|
||||
|
||||
const chartData = { lastInteractionTime, maxCommitDuration };
|
||||
|
||||
cachedChartData.set(rootID, chartData);
|
||||
|
||||
return chartData;
|
||||
}
|
||||
|
||||
export function invalidateChartData(): void {
|
||||
cachedChartData.clear();
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export default function SidebarInteractions(_: Props) {
|
||||
return <div className={styles.NothingSelected}>Nothing selected</div>;
|
||||
}
|
||||
|
||||
const { interactions } = profilingCache.Interactions.read({
|
||||
const interactions = profilingCache.Interactions.read({
|
||||
rendererID: ((rendererID: any): number),
|
||||
rootID: ((rootID: any): number),
|
||||
});
|
||||
|
||||
@@ -25,10 +25,7 @@ export type InteractionWithCommits = {|
|
||||
commits: Array<number>,
|
||||
|};
|
||||
|
||||
export type Interactions = {|
|
||||
interactions: Array<InteractionWithCommits>,
|
||||
rootID: number,
|
||||
|};
|
||||
export type Interactions = Array<InteractionWithCommits>;
|
||||
|
||||
export type CommitDetails = {|
|
||||
actualDurations: Map<number, number>,
|
||||
|
||||
Reference in New Issue
Block a user