GraphNG: fix mem leaks & avoid plot re-inits (#38017)

* State timeline: fix mem leak caused by excessive plot re-init

* Update PlotTooltipInterpolator type

* Do not reference config object in the setCursor hook

* fix excessive BarChart re-init caused by shallow diff of text config

* one less error

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Leon Sorokin
2021-08-18 13:43:53 -05:00
committed by GitHub
parent 63a85a4ac0
commit 49b129b110
7 changed files with 86 additions and 53 deletions

View File

@@ -12,6 +12,7 @@ import {
} from '@grafana/ui';
import { BarChartOptions } from './types';
import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
import { PropDiffFn } from '../../../../../packages/grafana-ui/src/components/GraphNG/GraphNG';
/**
* @alpha
@@ -20,7 +21,14 @@ export interface BarChartProps
extends BarChartOptions,
Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend' | 'theme'> {}
const propsToDiff: string[] = ['orientation', 'barWidth', 'groupWidth', 'stacking', 'showValue', 'text'];
const propsToDiff: Array<string | PropDiffFn> = [
'orientation',
'barWidth',
'groupWidth',
'stacking',
'showValue',
(prev: BarChartProps, next: BarChartProps) => next.text?.valueSize === prev.text?.valueSize,
];
export const BarChart: React.FC<BarChartProps> = (props) => {
const theme = useTheme2();

View File

@@ -272,22 +272,22 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
const interpolateTooltip: PlotTooltipInterpolator = (
updateActiveSeriesIdx,
updateActiveDatapointIdx,
updateTooltipPosition
updateTooltipPosition,
u
) => {
return (u: uPlot) => {
let found: Rect | undefined;
let cx = u.cursor.left! * devicePixelRatio;
let cy = u.cursor.top! * devicePixelRatio;
let found: Rect | undefined;
let cx = u.cursor.left! * devicePixelRatio;
let cy = u.cursor.top! * devicePixelRatio;
qt.get(cx, cy, 1, 1, (o) => {
if (pointWithin(cx, cy, o.x, o.y, o.x + o.w, o.y + o.h)) {
found = o;
}
});
qt.get(cx, cy, 1, 1, (o) => {
if (pointWithin(cx, cy, o.x, o.y, o.x + o.w, o.y + o.h)) {
found = o;
}
});
if (found) {
// prettier-ignore
if (found !== hovered) {
if (found) {
// prettier-ignore
if (found !== hovered) {
barMark.style.display = '';
barMark.style.left = found.x / devicePixelRatio + 'px';
barMark.style.top = found.y / devicePixelRatio + 'px';
@@ -298,16 +298,15 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
updateActiveDatapointIdx(hovered.didx);
updateTooltipPosition();
}
} else if (hovered !== undefined) {
updateActiveSeriesIdx(hovered!.sidx);
updateActiveDatapointIdx(hovered!.didx);
updateTooltipPosition();
hovered = undefined;
barMark.style.display = 'none';
} else {
updateTooltipPosition(true);
}
};
} else if (hovered !== undefined) {
updateActiveSeriesIdx(hovered!.sidx);
updateActiveDatapointIdx(hovered!.didx);
updateTooltipPosition();
hovered = undefined;
barMark.style.display = 'none';
} else {
updateTooltipPosition(true);
}
};
let alignedTotals: AlignedData | null = null;

View File

@@ -126,7 +126,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
updateActiveSeriesIdx,
updateActiveDatapointIdx,
updateTooltipPosition
) => (u: uPlot) => {
) => {
if (shouldChangeHover) {
if (hoveredSeriesIdx != null) {
updateActiveSeriesIdx(hoveredSeriesIdx);
@@ -271,7 +271,7 @@ export function prepareTimelineFields(
let isTimeseries = false;
let changed = false;
const fields: Field[] = [];
for (const field of frame.fields) {
for (let field of frame.fields) {
switch (field.type) {
case FieldType.time:
isTimeseries = true;
@@ -281,8 +281,17 @@ export function prepareTimelineFields(
case FieldType.number:
case FieldType.boolean:
case FieldType.string:
// magic value for join() to leave nulls alone
(field.config.custom = field.config.custom ?? {}).spanNulls = -1;
field = {
...field,
config: {
...field.config,
custom: {
...field.config.custom,
// magic value for join() to leave nulls alone
spanNulls: -1,
},
},
};
if (mergeValues) {
let merged = unsetSameFutureValues(field.values.toArray());