mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
33e54fa252
Stacked on #30490. This is in the same spirit but to clarify the difference between what is React Native vs part of any generic Host. We used to use "Native" to mean three different concepts. Now "Native" just means React Native. E.g. from the frontend's perspective the Host can be Highlighted/Inspected. However, that in turn can then be implemented as either direct DOM manipulation or commands to React Native. So frontend -> backend is "Host" but backend -> React Native is "Native" while backend -> DOM is "Web". Rename NativeElementsPanel to BuiltinElementsPanel. This isn't a React Native panel but one part of the surrounding DevTools. We refer to Host more as the thing running React itself. I.e. where the backend lives. The runtime you're inspecting. The DevTools itself needs a third term. So I went with "Builtin".
181 lines
5.1 KiB
JavaScript
181 lines
5.1 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import * as React from 'react';
|
|
import {useCallback, useContext, useMemo, useState} from 'react';
|
|
import AutoSizer from 'react-virtualized-auto-sizer';
|
|
import {FixedSizeList} from 'react-window';
|
|
import {ProfilerContext} from './ProfilerContext';
|
|
import NoCommitData from './NoCommitData';
|
|
import CommitRankedListItem from './CommitRankedListItem';
|
|
import HoveredFiberInfo from './HoveredFiberInfo';
|
|
import {scale} from './utils';
|
|
import {StoreContext} from '../context';
|
|
import {SettingsContext} from '../Settings/SettingsContext';
|
|
import {useHighlightHostInstance} from '../hooks';
|
|
import Tooltip from './Tooltip';
|
|
|
|
import styles from './CommitRanked.css';
|
|
|
|
import type {TooltipFiberData} from './HoveredFiberInfo';
|
|
import type {ChartData} from './RankedChartBuilder';
|
|
import type {CommitTree} from './types';
|
|
|
|
export type ItemData = {
|
|
chartData: ChartData,
|
|
onElementMouseEnter: (fiberData: TooltipFiberData) => void,
|
|
onElementMouseLeave: () => void,
|
|
scaleX: (value: number, fallbackValue: number) => number,
|
|
selectedFiberID: number | null,
|
|
selectedFiberIndex: number,
|
|
selectFiber: (id: number | null, name: string | null) => void,
|
|
width: number,
|
|
};
|
|
|
|
export default function CommitRankedAutoSizer(_: {}): React.Node {
|
|
const {profilerStore} = useContext(StoreContext);
|
|
const {rootID, selectedCommitIndex, selectFiber} =
|
|
useContext(ProfilerContext);
|
|
const {profilingCache} = profilerStore;
|
|
|
|
const deselectCurrentFiber = useCallback(
|
|
(event: $FlowFixMe) => {
|
|
event.stopPropagation();
|
|
selectFiber(null, null);
|
|
},
|
|
[selectFiber],
|
|
);
|
|
|
|
let commitTree: CommitTree | null = null;
|
|
let chartData: ChartData | null = null;
|
|
if (selectedCommitIndex !== null) {
|
|
commitTree = profilingCache.getCommitTree({
|
|
commitIndex: selectedCommitIndex,
|
|
rootID: ((rootID: any): number),
|
|
});
|
|
|
|
chartData = profilingCache.getRankedChartData({
|
|
commitIndex: selectedCommitIndex,
|
|
commitTree,
|
|
rootID: ((rootID: any): number),
|
|
});
|
|
}
|
|
|
|
if (commitTree != null && chartData != null && chartData.nodes.length > 0) {
|
|
return (
|
|
<div className={styles.Container} onClick={deselectCurrentFiber}>
|
|
<AutoSizer>
|
|
{({height, width}) => (
|
|
<CommitRanked
|
|
chartData={((chartData: any): ChartData)}
|
|
commitTree={((commitTree: any): CommitTree)}
|
|
height={height}
|
|
width={width}
|
|
/>
|
|
)}
|
|
</AutoSizer>
|
|
</div>
|
|
);
|
|
} else {
|
|
return <NoCommitData />;
|
|
}
|
|
}
|
|
|
|
type Props = {
|
|
chartData: ChartData,
|
|
commitTree: CommitTree,
|
|
height: number,
|
|
width: number,
|
|
};
|
|
|
|
function CommitRanked({chartData, commitTree, height, width}: Props) {
|
|
const [hoveredFiberData, setHoveredFiberData] =
|
|
useState<TooltipFiberData | null>(null);
|
|
const {lineHeight} = useContext(SettingsContext);
|
|
const {selectedFiberID, selectFiber} = useContext(ProfilerContext);
|
|
const {highlightHostInstance, clearHighlightHostInstance} =
|
|
useHighlightHostInstance();
|
|
|
|
const selectedFiberIndex = useMemo(
|
|
() => getNodeIndex(chartData, selectedFiberID),
|
|
[chartData, selectedFiberID],
|
|
);
|
|
|
|
const handleElementMouseEnter = useCallback(
|
|
({id, name}: $FlowFixMe) => {
|
|
highlightHostInstance(id); // Highlight last hovered element.
|
|
setHoveredFiberData({id, name}); // Set hovered fiber data for tooltip
|
|
},
|
|
[highlightHostInstance],
|
|
);
|
|
|
|
const handleElementMouseLeave = useCallback(() => {
|
|
clearHighlightHostInstance(); // clear highlighting of element on mouse leave
|
|
setHoveredFiberData(null); // clear hovered fiber data for tooltip
|
|
}, [clearHighlightHostInstance]);
|
|
|
|
const itemData = useMemo<ItemData>(
|
|
() => ({
|
|
chartData,
|
|
onElementMouseEnter: handleElementMouseEnter,
|
|
onElementMouseLeave: handleElementMouseLeave,
|
|
scaleX: scale(0, chartData.nodes[selectedFiberIndex].value, 0, width),
|
|
selectedFiberID,
|
|
selectedFiberIndex,
|
|
selectFiber,
|
|
width,
|
|
}),
|
|
[
|
|
chartData,
|
|
handleElementMouseEnter,
|
|
handleElementMouseLeave,
|
|
selectedFiberID,
|
|
selectedFiberIndex,
|
|
selectFiber,
|
|
width,
|
|
],
|
|
);
|
|
|
|
// Tooltip used to show summary of fiber info on hover
|
|
const tooltipLabel = useMemo(
|
|
() =>
|
|
hoveredFiberData !== null ? (
|
|
<HoveredFiberInfo fiberData={hoveredFiberData} />
|
|
) : null,
|
|
[hoveredFiberData],
|
|
);
|
|
|
|
return (
|
|
<Tooltip label={tooltipLabel}>
|
|
<FixedSizeList
|
|
height={height}
|
|
innerElementType="svg"
|
|
itemCount={chartData.nodes.length}
|
|
itemData={itemData}
|
|
itemSize={lineHeight}
|
|
width={width}>
|
|
{CommitRankedListItem}
|
|
</FixedSizeList>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
|
|
const getNodeIndex = (chartData: ChartData, id: number | null): number => {
|
|
if (id === null) {
|
|
return 0;
|
|
}
|
|
const {nodes} = chartData;
|
|
for (let index = 0; index < nodes.length; index++) {
|
|
if (nodes[index].id === id) {
|
|
return index;
|
|
}
|
|
}
|
|
return 0;
|
|
};
|