mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactor UNSAFE_component... from SpanGraph and VirtualizedTraceView (#37418)
This commit is contained in:
parent
bc6e7d32e2
commit
0699a04dcd
@ -73,4 +73,16 @@ describe('<SpanGraph>', () => {
|
|||||||
}));
|
}));
|
||||||
expect(canvasGraph.prop('items')).toEqual(items);
|
expect(canvasGraph.prop('items')).toEqual(items);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not regenerate CanvasSpanGraph without new trace', () => {
|
||||||
|
const canvasGraph = wrapper.find(CanvasSpanGraph).first();
|
||||||
|
const items = canvasGraph.prop('items');
|
||||||
|
|
||||||
|
wrapper.instance().forceUpdate();
|
||||||
|
|
||||||
|
const newCanvasGraph = wrapper.find(CanvasSpanGraph).first();
|
||||||
|
const newItems = newCanvasGraph.prop('items');
|
||||||
|
|
||||||
|
expect(newItems).toBe(items);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
import memoizeOne from 'memoize-one';
|
||||||
|
|
||||||
import CanvasSpanGraph from './CanvasSpanGraph';
|
import CanvasSpanGraph from './CanvasSpanGraph';
|
||||||
import TickLabels from './TickLabels';
|
import TickLabels from './TickLabels';
|
||||||
@ -33,19 +34,13 @@ type SpanGraphProps = {
|
|||||||
updateNextViewRangeTime: (nextUpdate: ViewRangeTimeUpdate) => void;
|
updateNextViewRangeTime: (nextUpdate: ViewRangeTimeUpdate) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
type SpanItem = {
|
||||||
* Store `items` in state so they are not regenerated every render. Otherwise,
|
|
||||||
* the canvas graph will re-render itself every time.
|
|
||||||
*/
|
|
||||||
type SpanGraphState = {
|
|
||||||
items: Array<{
|
|
||||||
valueOffset: number;
|
valueOffset: number;
|
||||||
valueWidth: number;
|
valueWidth: number;
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
}>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getItem(span: TraceSpan) {
|
function getItem(span: TraceSpan): SpanItem {
|
||||||
return {
|
return {
|
||||||
valueOffset: span.relativeStartTime,
|
valueOffset: span.relativeStartTime,
|
||||||
valueWidth: span.duration,
|
valueWidth: span.duration,
|
||||||
@ -53,36 +48,24 @@ function getItem(span: TraceSpan) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SpanGraph extends React.PureComponent<SpanGraphProps, SpanGraphState> {
|
function getItems(trace: Trace): SpanItem[] {
|
||||||
state: SpanGraphState;
|
return trace.spans.map(getItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoizedGetitems = memoizeOne(getItems);
|
||||||
|
|
||||||
|
export default class SpanGraph extends React.PureComponent<SpanGraphProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
height: DEFAULT_HEIGHT,
|
height: DEFAULT_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: SpanGraphProps) {
|
|
||||||
super(props);
|
|
||||||
const { trace } = props;
|
|
||||||
this.state = {
|
|
||||||
items: trace ? trace.spans.map(getItem) : [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps: SpanGraphProps) {
|
|
||||||
const { trace } = nextProps;
|
|
||||||
if (this.props.trace !== trace) {
|
|
||||||
this.setState({
|
|
||||||
items: trace ? trace.spans.map(getItem) : [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { height, trace, viewRange, updateNextViewRangeTime, updateViewRangeTime } = this.props;
|
const { height, trace, viewRange, updateNextViewRangeTime, updateViewRangeTime } = this.props;
|
||||||
if (!trace) {
|
if (!trace) {
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
const { items } = this.state;
|
|
||||||
|
const items = memoizedGetitems(trace);
|
||||||
return (
|
return (
|
||||||
<div className={cx(ubPb2, ubPx2)}>
|
<div className={cx(ubPb2, ubPx2)}>
|
||||||
<TickLabels numTicks={TIMELINE_TICK_INTERVAL} duration={trace.duration} />
|
<TickLabels numTicks={TIMELINE_TICK_INTERVAL} duration={trace.duration} />
|
||||||
|
@ -300,7 +300,8 @@ describe('<VirtualizedTraceViewImpl>', () => {
|
|||||||
expect(
|
expect(
|
||||||
rowWrapper.containsMatchingElement(
|
rowWrapper.containsMatchingElement(
|
||||||
<SpanBarRow
|
<SpanBarRow
|
||||||
className={instance.clippingCssClasses}
|
clippingLeft={instance.getClipping().left}
|
||||||
|
clippingRight={instance.getClipping().right}
|
||||||
columnDivision={props.spanNameColumnWidth}
|
columnDivision={props.spanNameColumnWidth}
|
||||||
isChildrenExpanded
|
isChildrenExpanded
|
||||||
isDetailExpanded={false}
|
isDetailExpanded={false}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
import memoizeOne from 'memoize-one';
|
||||||
import ListView from './ListView';
|
import ListView from './ListView';
|
||||||
import SpanBarRow from './SpanBarRow';
|
import SpanBarRow from './SpanBarRow';
|
||||||
import DetailState from './SpanDetail/DetailState';
|
import DetailState from './SpanDetail/DetailState';
|
||||||
@ -148,28 +150,25 @@ function getClipping(currentViewRange: [number, number]) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateRowStatesFromTrace(
|
||||||
|
trace: Trace | TNil,
|
||||||
|
childrenHiddenIDs: Set<string>,
|
||||||
|
detailStates: Map<string, DetailState | TNil>
|
||||||
|
): RowState[] {
|
||||||
|
return trace ? generateRowStates(trace.spans, childrenHiddenIDs, detailStates) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoizedGenerateRowStates = memoizeOne(generateRowStatesFromTrace);
|
||||||
|
const memoizedViewBoundsFunc = memoizeOne(createViewedBoundsFunc, isEqual);
|
||||||
|
const memoizedGetClipping = memoizeOne(getClipping, isEqual);
|
||||||
|
|
||||||
// export from tests
|
// export from tests
|
||||||
export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTraceViewProps> {
|
export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTraceViewProps> {
|
||||||
clipping: { left: boolean; right: boolean };
|
|
||||||
listView: ListView | TNil;
|
listView: ListView | TNil;
|
||||||
rowStates: RowState[];
|
|
||||||
getViewedBounds: ViewedBoundsFunctionType;
|
|
||||||
|
|
||||||
constructor(props: VirtualizedTraceViewProps) {
|
constructor(props: VirtualizedTraceViewProps) {
|
||||||
super(props);
|
super(props);
|
||||||
// keep "prop derivations" on the instance instead of calculating in
|
const { setTrace, trace, uiFind } = props;
|
||||||
// `.render()` to avoid recalculating in every invocation of `.renderRow()`
|
|
||||||
const { currentViewRangeTime, childrenHiddenIDs, detailStates, setTrace, trace, uiFind } = props;
|
|
||||||
this.clipping = getClipping(currentViewRangeTime);
|
|
||||||
const [zoomStart, zoomEnd] = currentViewRangeTime;
|
|
||||||
this.getViewedBounds = createViewedBoundsFunc({
|
|
||||||
min: trace.startTime,
|
|
||||||
max: trace.endTime,
|
|
||||||
viewStart: zoomStart,
|
|
||||||
viewEnd: zoomEnd,
|
|
||||||
});
|
|
||||||
this.rowStates = generateRowStates(trace.spans, childrenHiddenIDs, detailStates);
|
|
||||||
|
|
||||||
setTrace(trace, uiFind);
|
setTrace(trace, uiFind);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,50 +190,54 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillUpdate(nextProps: VirtualizedTraceViewProps) {
|
componentDidUpdate(prevProps: Readonly<VirtualizedTraceViewProps>) {
|
||||||
const { childrenHiddenIDs, detailStates, registerAccessors, trace, currentViewRangeTime } = this.props;
|
const { registerAccessors, trace } = prevProps;
|
||||||
const {
|
|
||||||
currentViewRangeTime: nextViewRangeTime,
|
|
||||||
childrenHiddenIDs: nextHiddenIDs,
|
|
||||||
detailStates: nextDetailStates,
|
|
||||||
registerAccessors: nextRegisterAccessors,
|
|
||||||
setTrace,
|
|
||||||
trace: nextTrace,
|
|
||||||
uiFind,
|
|
||||||
} = nextProps;
|
|
||||||
if (trace !== nextTrace) {
|
|
||||||
setTrace(nextTrace, uiFind);
|
|
||||||
}
|
|
||||||
if (trace !== nextTrace || childrenHiddenIDs !== nextHiddenIDs || detailStates !== nextDetailStates) {
|
|
||||||
this.rowStates = nextTrace ? generateRowStates(nextTrace.spans, nextHiddenIDs, nextDetailStates) : [];
|
|
||||||
}
|
|
||||||
if (currentViewRangeTime !== nextViewRangeTime || (trace !== nextTrace && nextTrace)) {
|
|
||||||
this.clipping = getClipping(nextViewRangeTime);
|
|
||||||
const [zoomStart, zoomEnd] = nextViewRangeTime;
|
|
||||||
this.getViewedBounds = createViewedBoundsFunc({
|
|
||||||
min: nextTrace.startTime,
|
|
||||||
max: nextTrace.endTime,
|
|
||||||
viewStart: zoomStart,
|
|
||||||
viewEnd: zoomEnd,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.listView && registerAccessors !== nextRegisterAccessors) {
|
|
||||||
nextRegisterAccessors(this.getAccessors());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
const {
|
const {
|
||||||
shouldScrollToFirstUiFindMatch,
|
shouldScrollToFirstUiFindMatch,
|
||||||
clearShouldScrollToFirstUiFindMatch,
|
clearShouldScrollToFirstUiFindMatch,
|
||||||
scrollToFirstVisibleSpan,
|
scrollToFirstVisibleSpan,
|
||||||
|
registerAccessors: nextRegisterAccessors,
|
||||||
|
setTrace,
|
||||||
|
trace: nextTrace,
|
||||||
|
uiFind,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
if (trace !== nextTrace) {
|
||||||
|
setTrace(nextTrace, uiFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.listView && registerAccessors !== nextRegisterAccessors) {
|
||||||
|
nextRegisterAccessors(this.getAccessors());
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldScrollToFirstUiFindMatch) {
|
if (shouldScrollToFirstUiFindMatch) {
|
||||||
scrollToFirstVisibleSpan();
|
scrollToFirstVisibleSpan();
|
||||||
clearShouldScrollToFirstUiFindMatch();
|
clearShouldScrollToFirstUiFindMatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRowStates(): RowState[] {
|
||||||
|
const { childrenHiddenIDs, detailStates, trace } = this.props;
|
||||||
|
return memoizedGenerateRowStates(trace, childrenHiddenIDs, detailStates);
|
||||||
|
}
|
||||||
|
|
||||||
|
getClipping(): { left: boolean; right: boolean } {
|
||||||
|
const { currentViewRangeTime } = this.props;
|
||||||
|
return memoizedGetClipping(currentViewRangeTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewedBounds(): ViewedBoundsFunctionType {
|
||||||
|
const { currentViewRangeTime, trace } = this.props;
|
||||||
|
const [zoomStart, zoomEnd] = currentViewRangeTime;
|
||||||
|
|
||||||
|
return memoizedViewBoundsFunc({
|
||||||
|
min: trace.startTime,
|
||||||
|
max: trace.endTime,
|
||||||
|
viewStart: zoomStart,
|
||||||
|
viewEnd: zoomEnd,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getAccessors() {
|
getAccessors() {
|
||||||
const lv = this.listView;
|
const lv = this.listView;
|
||||||
if (!lv) {
|
if (!lv) {
|
||||||
@ -259,12 +262,12 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
|
|
||||||
getCollapsedChildren = () => this.props.childrenHiddenIDs;
|
getCollapsedChildren = () => this.props.childrenHiddenIDs;
|
||||||
|
|
||||||
mapRowIndexToSpanIndex = (index: number) => this.rowStates[index].spanIndex;
|
mapRowIndexToSpanIndex = (index: number) => this.getRowStates()[index].spanIndex;
|
||||||
|
|
||||||
mapSpanIndexToRowIndex = (index: number) => {
|
mapSpanIndexToRowIndex = (index: number) => {
|
||||||
const max = this.rowStates.length;
|
const max = this.getRowStates().length;
|
||||||
for (let i = 0; i < max; i++) {
|
for (let i = 0; i < max; i++) {
|
||||||
const { spanIndex } = this.rowStates[i];
|
const { spanIndex } = this.getRowStates()[i];
|
||||||
if (spanIndex === index) {
|
if (spanIndex === index) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -283,7 +286,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
// use long form syntax to avert flow error
|
// use long form syntax to avert flow error
|
||||||
// https://github.com/facebook/flow/issues/3076#issuecomment-290944051
|
// https://github.com/facebook/flow/issues/3076#issuecomment-290944051
|
||||||
getKeyFromIndex = (index: number) => {
|
getKeyFromIndex = (index: number) => {
|
||||||
const { isDetail, span } = this.rowStates[index];
|
const { isDetail, span } = this.getRowStates()[index];
|
||||||
return `${span.spanID}--${isDetail ? 'detail' : 'bar'}`;
|
return `${span.spanID}--${isDetail ? 'detail' : 'bar'}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -291,9 +294,9 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
const parts = key.split('--');
|
const parts = key.split('--');
|
||||||
const _spanID = parts[0];
|
const _spanID = parts[0];
|
||||||
const _isDetail = parts[1] === 'detail';
|
const _isDetail = parts[1] === 'detail';
|
||||||
const max = this.rowStates.length;
|
const max = this.getRowStates().length;
|
||||||
for (let i = 0; i < max; i++) {
|
for (let i = 0; i < max; i++) {
|
||||||
const { span, isDetail } = this.rowStates[i];
|
const { span, isDetail } = this.getRowStates()[i];
|
||||||
if (span.spanID === _spanID && isDetail === _isDetail) {
|
if (span.spanID === _spanID && isDetail === _isDetail) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -302,7 +305,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
};
|
};
|
||||||
|
|
||||||
getRowHeight = (index: number) => {
|
getRowHeight = (index: number) => {
|
||||||
const { span, isDetail } = this.rowStates[index];
|
const { span, isDetail } = this.getRowStates()[index];
|
||||||
if (!isDetail) {
|
if (!isDetail) {
|
||||||
return DEFAULT_HEIGHTS.bar;
|
return DEFAULT_HEIGHTS.bar;
|
||||||
}
|
}
|
||||||
@ -313,7 +316,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderRow = (key: string, style: React.CSSProperties, index: number, attrs: {}) => {
|
renderRow = (key: string, style: React.CSSProperties, index: number, attrs: {}) => {
|
||||||
const { isDetail, span, spanIndex } = this.rowStates[index];
|
const { isDetail, span, spanIndex } = this.getRowStates()[index];
|
||||||
return isDetail
|
return isDetail
|
||||||
? this.renderSpanDetailRow(span, key, style, attrs)
|
? this.renderSpanDetailRow(span, key, style, attrs)
|
||||||
: this.renderSpanBarRow(span, spanIndex, key, style, attrs);
|
: this.renderSpanBarRow(span, spanIndex, key, style, attrs);
|
||||||
@ -352,7 +355,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
const rpcSpan = findServerChildSpan(trace.spans.slice(spanIndex));
|
const rpcSpan = findServerChildSpan(trace.spans.slice(spanIndex));
|
||||||
if (rpcSpan) {
|
if (rpcSpan) {
|
||||||
const rpcViewBounds = this.getViewedBounds(rpcSpan.startTime, rpcSpan.startTime + rpcSpan.duration);
|
const rpcViewBounds = this.getViewedBounds()(rpcSpan.startTime, rpcSpan.startTime + rpcSpan.duration);
|
||||||
rpc = {
|
rpc = {
|
||||||
color: getColorByKey(rpcSpan.process.serviceName, theme),
|
color: getColorByKey(rpcSpan.process.serviceName, theme),
|
||||||
operationName: rpcSpan.operationName,
|
operationName: rpcSpan.operationName,
|
||||||
@ -378,8 +381,8 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
return (
|
return (
|
||||||
<div className={styles.row} key={key} style={style} {...attrs}>
|
<div className={styles.row} key={key} style={style} {...attrs}>
|
||||||
<SpanBarRow
|
<SpanBarRow
|
||||||
clippingLeft={this.clipping.left}
|
clippingLeft={this.getClipping().left}
|
||||||
clippingRight={this.clipping.right}
|
clippingRight={this.getClipping().right}
|
||||||
color={color}
|
color={color}
|
||||||
columnDivision={spanNameColumnWidth}
|
columnDivision={spanNameColumnWidth}
|
||||||
isChildrenExpanded={!isCollapsed}
|
isChildrenExpanded={!isCollapsed}
|
||||||
@ -391,7 +394,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
rpc={rpc}
|
rpc={rpc}
|
||||||
noInstrumentedServer={noInstrumentedServer}
|
noInstrumentedServer={noInstrumentedServer}
|
||||||
showErrorIcon={showErrorIcon}
|
showErrorIcon={showErrorIcon}
|
||||||
getViewedBounds={this.getViewedBounds}
|
getViewedBounds={this.getViewedBounds()}
|
||||||
traceStartTime={trace.startTime}
|
traceStartTime={trace.startTime}
|
||||||
span={span}
|
span={span}
|
||||||
focusSpan={focusSpan}
|
focusSpan={focusSpan}
|
||||||
@ -467,7 +470,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
|
|||||||
<div>
|
<div>
|
||||||
<ListView
|
<ListView
|
||||||
ref={this.setListView}
|
ref={this.setListView}
|
||||||
dataLength={this.rowStates.length}
|
dataLength={this.getRowStates().length}
|
||||||
itemHeightGetter={this.getRowHeight}
|
itemHeightGetter={this.getRowHeight}
|
||||||
itemRenderer={this.renderRow}
|
itemRenderer={this.renderRow}
|
||||||
viewBuffer={50}
|
viewBuffer={50}
|
||||||
|
Loading…
Reference in New Issue
Block a user