diff --git a/packages/scenes/src/components/VizPanel/VizPanel.tsx b/packages/scenes/src/components/VizPanel/VizPanel.tsx index 12b1bb044..ce2ecfc13 100644 --- a/packages/scenes/src/components/VizPanel/VizPanel.tsx +++ b/packages/scenes/src/components/VizPanel/VizPanel.tsx @@ -91,6 +91,18 @@ export interface VizPanelState extends SceneOb */ extendPanelContext?: (vizPanel: VizPanel, context: PanelContext) => void; + /** + * @internal + * experimental / temporary + * + * clears field.values arrays of previous/stale/retained series and annotations frames in PanelData + * + * see https://github.com/facebook/react/issues/36176 + * see https://github.com/grafana/grafana/pull/121682 + * see https://github.com/grafana/grafana/pull/120190 + **/ + _UNSAFE_clearPreviousFieldValues?: boolean; + /** * Sets panel chrome collapsed state */ diff --git a/packages/scenes/src/components/VizPanel/VizPanelRenderer.tsx b/packages/scenes/src/components/VizPanel/VizPanelRenderer.tsx index ac2028082..7bb768585 100644 --- a/packages/scenes/src/components/VizPanel/VizPanelRenderer.tsx +++ b/packages/scenes/src/components/VizPanel/VizPanelRenderer.tsx @@ -1,9 +1,16 @@ import { Trans } from '@grafana/i18n'; -import React, { RefCallback, useCallback, useEffect, useLayoutEffect, useMemo } from 'react'; -import { useMeasure } from 'react-use'; +import React, { RefCallback, useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'; +import { useMeasure, usePrevious } from 'react-use'; // @ts-ignore -import { AlertState, GrafanaTheme2, PanelData, PluginContextProvider, SetPanelAttentionEvent } from '@grafana/data'; +import { + AlertState, + DataFrame, + GrafanaTheme2, + PanelData, + PluginContextProvider, + SetPanelAttentionEvent, +} from '@grafana/data'; import { getAppEvents } from '@grafana/runtime'; import { PanelChrome, ErrorBoundaryAlert, PanelContextProvider, Tooltip, useStyles2, Icon } from '@grafana/ui'; @@ -37,6 +44,7 @@ export function VizPanelRenderer({ model }: SceneComponentProps) { collapsible, collapsed, _renderCounter = 0, + _UNSAFE_clearPreviousFieldValues = false, } = model.useState(); const [ref, { width, height }] = useMeasure(); const appEvents = useMemo(() => getAppEvents(), []); @@ -85,6 +93,11 @@ export function VizPanelRenderer({ model }: SceneComponentProps) { const dataObject = sceneGraph.getData(model); const rawData = dataObject.useState(); + + const { series, annotations } = _UNSAFE_clearPreviousFieldValues ? rawData.data ?? {} : {}; + useClearPreviousData(series); + useClearPreviousData(annotations); + const dataWithSeriesLimit = useDataWithSeriesLimit(rawData.data, seriesLimit, seriesLimitShowAll); const dataWithFieldConfig = model.applyFieldConfig(dataWithSeriesLimit); const sceneTimeRange = sceneGraph.getTimeRange(model); @@ -305,6 +318,41 @@ export function VizPanelRenderer({ model }: SceneComponentProps) { ); } +function useClearPreviousData(data?: DataFrame[]) { + // this holds all value arrays from all series or anno frames + // so we can empty any previous ones that no longer appear in current data + // why? because React fiber: https://github.com/facebook/react/issues/36176 + const prevVals = useRef>(); + const currVals = useRef>(); + prevVals.current ??= new Set(); + currVals.current ??= new Set(); + + const currFrames = data; + const prevFrames = usePrevious(currFrames); + + if (currFrames != null && currFrames !== prevFrames) { + // populate new + currVals.current.clear(); + + for (let i = 0; i < currFrames.length; i++) { + let fields = currFrames[i].fields; + + for (let j = 0; j < fields.length; j++) { + currVals.current.add(fields[j].values); + } + } + + // empty out all prev not seen in new + prevVals.current.forEach((vals) => { + if (!currVals.current!.has(vals)) { + vals.length = 0; + } + }); + prevVals.current.clear(); + prevVals.current = new Set(currVals.current); + } +} + function useDataWithSeriesLimit(data: PanelData | undefined, seriesLimit?: number, showAllSeries?: boolean) { return useMemo(() => { if (!data?.series || !seriesLimit || showAllSeries) {