react/packages/react-devtools-shared/src/devtools/views/Profiler/CommitFlamegraph.js

166 lines
4.5 KiB
JavaScript
Raw Normal View History

2019-03-16 11:05:52 +08:00
// @flow
2019-08-14 08:58:03 +08:00
import React, {forwardRef, useCallback, useContext, useMemo} from 'react';
2019-03-18 00:49:10 +08:00
import AutoSizer from 'react-virtualized-auto-sizer';
2019-08-14 08:58:03 +08:00
import {FixedSizeList} from 'react-window';
import {ProfilerContext} from './ProfilerContext';
2019-03-18 00:49:10 +08:00
import NoCommitData from './NoCommitData';
import CommitFlamegraphListItem from './CommitFlamegraphListItem';
2019-08-14 08:58:03 +08:00
import {scale} from './utils';
import {StoreContext} from '../context';
import {SettingsContext} from '../Settings/SettingsContext';
2019-03-16 11:05:52 +08:00
import styles from './CommitFlamegraph.css';
2019-08-14 08:58:03 +08:00
import type {ChartData, ChartNode} from './FlamegraphChartBuilder';
import type {CommitTree} from './types';
2019-03-18 00:49:10 +08:00
export type ItemData = {|
chartData: ChartData,
scaleX: (value: number, fallbackValue: number) => number,
selectedChartNode: ChartNode | null,
2019-03-18 00:49:10 +08:00
selectedChartNodeIndex: number,
selectFiber: (id: number | null, name: string | null) => void,
2019-03-18 00:49:10 +08:00
width: number,
|};
export default function CommitFlamegraphAutoSizer(_: {||}) {
2019-08-14 08:58:03 +08:00
const {profilerStore} = useContext(StoreContext);
const {rootID, selectedCommitIndex, selectFiber} = useContext(
ProfilerContext,
);
2019-08-14 08:58:03 +08:00
const {profilingCache} = profilerStore;
2019-03-18 00:49:10 +08:00
const deselectCurrentFiber = useCallback(
event => {
event.stopPropagation();
selectFiber(null, null);
2019-03-18 00:49:10 +08:00
},
2019-08-14 08:58:03 +08:00
[selectFiber],
2019-03-16 11:05:52 +08:00
);
let commitTree: CommitTree | null = null;
let chartData: ChartData | null = null;
if (selectedCommitIndex !== null) {
commitTree = profilingCache.getCommitTree({
commitIndex: selectedCommitIndex,
rootID: ((rootID: any): number),
});
chartData = profilingCache.getFlamegraphChartData({
commitIndex: selectedCommitIndex,
commitTree,
rootID: ((rootID: any): number),
});
}
2019-03-16 11:05:52 +08:00
if (commitTree != null && chartData != null && chartData.depth > 0) {
return (
<div className={styles.Container} onClick={deselectCurrentFiber}>
<AutoSizer>
2019-08-14 08:58:03 +08:00
{({height, width}) => (
// Force Flow types to avoid checking for `null` here because there's no static proof that
// by the time this render prop function is called, the values of the `let` variables have not changed.
<CommitFlamegraph
chartData={((chartData: any): ChartData)}
commitTree={((commitTree: any): CommitTree)}
height={height}
width={width}
/>
)}
</AutoSizer>
</div>
);
} else {
return <NoCommitData />;
}
}
2019-03-16 11:05:52 +08:00
type Props = {|
chartData: ChartData,
commitTree: CommitTree,
height: number,
width: number,
|};
2019-08-14 08:58:03 +08:00
function CommitFlamegraph({chartData, commitTree, height, width}: Props) {
const {lineHeight} = useContext(SettingsContext);
const {selectFiber, selectedFiberID} = useContext(ProfilerContext);
const selectedChartNodeIndex = useMemo<number>(
() => {
if (selectedFiberID === null) {
return 0;
}
2019-08-14 08:58:03 +08:00
// The selected node might not be in the tree for this commit,
// so it's important that we have a fallback plan.
const depth = chartData.idToDepthMap.get(selectedFiberID);
return depth !== undefined ? depth - 1 : 0;
},
[chartData, selectedFiberID],
);
const selectedChartNode = useMemo(
() => {
if (selectedFiberID !== null) {
2019-08-14 12:59:07 +08:00
return (
chartData.rows[selectedChartNodeIndex].find(
chartNode => chartNode.id === selectedFiberID,
) || null
2019-08-14 08:58:03 +08:00
);
}
2019-08-14 12:59:07 +08:00
return null;
2019-08-14 08:58:03 +08:00
},
[chartData, selectedFiberID, selectedChartNodeIndex],
);
2019-03-18 00:49:10 +08:00
const itemData = useMemo<ItemData>(
() => ({
chartData,
scaleX: scale(
0,
selectedChartNode !== null
? selectedChartNode.treeBaseDuration
: chartData.baseDuration,
0,
2019-08-14 08:58:03 +08:00
width,
),
2019-03-18 00:49:10 +08:00
selectedChartNode,
selectedChartNodeIndex,
selectFiber,
width,
}),
2019-08-14 08:58:03 +08:00
[chartData, selectedChartNode, selectedChartNodeIndex, selectFiber, width],
2019-03-18 00:49:10 +08:00
);
return (
<FixedSizeList
height={height}
innerElementType={InnerElementType}
2019-03-18 00:49:10 +08:00
itemCount={chartData.depth}
itemData={itemData}
itemSize={lineHeight}
2019-08-14 08:58:03 +08:00
width={width}>
2019-03-18 00:49:10 +08:00
{CommitFlamegraphListItem}
</FixedSizeList>
);
2019-03-16 11:05:52 +08:00
}
2019-08-14 08:58:03 +08:00
const InnerElementType = forwardRef(({children, ...rest}, ref) => (
<svg ref={ref} {...rest}>
<defs>
<pattern
id="didNotRenderPattern"
patternUnits="userSpaceOnUse"
width="4"
2019-08-14 08:58:03 +08:00
height="4">
<path
d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2"
className={styles.PatternPath}
/>
</pattern>
</defs>
{children}
</svg>
));