diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.test.js b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.test.js index 88376f8ce7..e34c4d4cc0 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.test.js @@ -73,4 +73,16 @@ describe('', () => { })); 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); + }); }); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.tsx index aedaac67a4..8d76216632 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/SpanGraph/index.tsx @@ -13,6 +13,7 @@ // limitations under the License. import * as React from 'react'; +import memoizeOne from 'memoize-one'; import CanvasSpanGraph from './CanvasSpanGraph'; import TickLabels from './TickLabels'; @@ -31,19 +32,13 @@ type SpanGraphProps = { updateNextViewRangeTime: (nextUpdate: ViewRangeTimeUpdate) => void; }; -/** - * Store `items` in state so they are not regenerated every render. Otherwise, - * the canvas graph will re-render itself every time. - */ -type SpanGraphState = { - items: { - valueOffset: number; - valueWidth: number; - serviceName: string; - }[]; +type SpanItem = { + valueOffset: number; + valueWidth: number; + serviceName: string; }; -function getItem(span: Span) { +function getItem(span: Span): SpanItem { return { valueOffset: span.relativeStartTime, valueWidth: span.duration, @@ -51,37 +46,24 @@ function getItem(span: Span) { }; } -export default class SpanGraph extends React.PureComponent { - state: SpanGraphState; +function getItems(trace: Trace): SpanItem[] { + return trace.spans.map(getItem); +} + +const memoizedGetItems = memoizeOne(getItems); +export default class SpanGraph extends React.PureComponent { static defaultProps = { height: DEFAULT_HEIGHT, }; - constructor(props: SpanGraphProps) { - super(props); - const { trace } = props; - this.state = { - items: trace ? trace.spans.map(getItem) : [], - }; - } - - // eslint-disable-next-line camelcase - UNSAFE_componentWillReceiveProps(nextProps: SpanGraphProps) { - const { trace } = nextProps; - if (this.props.trace !== trace) { - this.setState({ - items: trace ? trace.spans.map(getItem) : [], - }); - } - } - render() { const { height, trace, viewRange, updateNextViewRangeTime, updateViewRangeTime } = this.props; if (!trace) { return
; } - const { items } = this.state; + + const items = memoizedGetItems(trace); return (