From 8d643d684df1e252e26be52362e9b5bb37b0ddca Mon Sep 17 00:00:00 2001 From: Andrei Zhaleznichenka Date: Wed, 26 Jul 2023 08:30:46 +0200 Subject: [PATCH] fix: Improve charts performance when fit-height is used (#1366) --- src/area-chart/model/use-chart-model.ts | 12 ++++------- .../container-queries/use-height-measure.ts | 20 +++++++++++++++++++ src/mixed-line-bar-chart/chart-container.tsx | 10 +++------- src/pie-chart/pie-chart.tsx | 9 ++------- 4 files changed, 29 insertions(+), 22 deletions(-) create mode 100644 src/internal/hooks/container-queries/use-height-measure.ts diff --git a/src/area-chart/model/use-chart-model.ts b/src/area-chart/model/use-chart-model.ts index aed775f809..b9602c7c85 100644 --- a/src/area-chart/model/use-chart-model.ts +++ b/src/area-chart/model/use-chart-model.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { AreaChartProps } from '../interfaces'; -import React, { useEffect, useMemo, useRef, RefObject, MouseEvent, useState } from 'react'; +import React, { useEffect, useMemo, useRef, RefObject, MouseEvent } from 'react'; import { findClosest, circleIndex } from './utils'; import { nodeContains } from '../../internal/utils/dom'; @@ -15,7 +15,7 @@ import { ChartModel } from './index'; import { ChartPlotRef } from '../../internal/components/chart-plot'; import { throttle } from '../../internal/utils/throttle'; import { useReaction } from '../async-store'; -import { useResizeObserver } from '../../internal/hooks/container-queries'; +import { useHeightMeasure } from '../../internal/hooks/container-queries/use-height-measure'; const MAX_HOVER_MARGIN = 6; const SVG_HOVER_THROTTLE = 25; @@ -59,12 +59,8 @@ export default function useChartModel({ const verticalMarkerRef = useRef(null); const plotMeasureRef = useRef(null); - const [measuredHeight, setHeight] = useState(0); - useResizeObserver( - () => plotMeasureRef.current, - entry => fitHeight && setHeight(entry.borderBoxHeight) - ); - const height = fitHeight ? measuredHeight : explicitHeight; + const hasVisibleSeries = series.length > 0; + const height = useHeightMeasure(() => plotMeasureRef.current, !fitHeight, [hasVisibleSeries]) ?? explicitHeight; const stableSetVisibleSeries = useStableEventHandler(setVisibleSeries); diff --git a/src/internal/hooks/container-queries/use-height-measure.ts b/src/internal/hooks/container-queries/use-height-measure.ts new file mode 100644 index 0000000000..95ad87721b --- /dev/null +++ b/src/internal/hooks/container-queries/use-height-measure.ts @@ -0,0 +1,20 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { useCallback, useState } from 'react'; +import { useResizeObserver } from './use-resize-observer'; + +/** + * Conditional resize observer for border box height used in charts. + */ +export function useHeightMeasure( + getMeasure: () => null | HTMLElement | SVGElement, + skip = false, + deps: React.DependencyList = [] +) { + const [measuredHeight, setHeight] = useState(0); + // eslint-disable-next-line react-hooks/exhaustive-deps + const stableGetMeasure = useCallback(getMeasure, deps); + useResizeObserver(stableGetMeasure, entry => !skip && setHeight(entry.borderBoxHeight)); + return !skip ? measuredHeight : undefined; +} diff --git a/src/mixed-line-bar-chart/chart-container.tsx b/src/mixed-line-bar-chart/chart-container.tsx index 5f6ddfb760..9f09421fa3 100644 --- a/src/mixed-line-bar-chart/chart-container.tsx +++ b/src/mixed-line-bar-chart/chart-container.tsx @@ -33,7 +33,7 @@ import useContainerWidth from '../internal/utils/use-container-width'; import { useMergeRefs } from '../internal/hooks/use-merge-refs'; import { nodeBelongs } from '../internal/utils/node-belongs'; import { CartesianChartContainer } from '../internal/components/cartesian-chart/chart-container'; -import { useResizeObserver } from '../internal/hooks/container-queries'; +import { useHeightMeasure } from '../internal/hooks/container-queries/use-height-measure'; const LEFT_LABELS_MARGIN = 16; const BOTTOM_LABELS_OFFSET = 12; @@ -122,12 +122,8 @@ export default function ChartContainer({ const popoverRef = useRef(null); const plotMeasureRef = useRef(null); - const [measuredHeight, setHeight] = useState(0); - useResizeObserver( - () => plotMeasureRef.current, - entry => fitHeight && setHeight(entry.borderBoxHeight) - ); - const plotHeight = fitHeight ? measuredHeight : explicitPlotHeight; + const measuredHeight = useHeightMeasure(() => plotMeasureRef.current, !fitHeight || !bottomLabelsHeight); + const plotHeight = fitHeight ? (bottomLabelsHeight ? measuredHeight ?? 0 : 0) : explicitPlotHeight; const isRefresh = useVisualRefresh(); diff --git a/src/pie-chart/pie-chart.tsx b/src/pie-chart/pie-chart.tsx index e515537f5c..55ecf33d82 100644 --- a/src/pie-chart/pie-chart.tsx +++ b/src/pie-chart/pie-chart.tsx @@ -20,8 +20,8 @@ import { SomeRequired } from '../internal/types'; import { useInternalI18n } from '../internal/i18n/context'; import { nodeBelongs } from '../internal/utils/node-belongs'; import clsx from 'clsx'; -import { useResizeObserver } from '../internal/hooks/container-queries'; import { useVisualRefresh } from '../internal/hooks/use-visual-mode'; +import { useHeightMeasure } from '../internal/hooks/container-queries/use-height-measure'; export interface InternalChartDatum { index: number; @@ -90,12 +90,7 @@ export default ({ const hasLabels = !(hideTitles && hideDescriptions); const isRefresh = useVisualRefresh(); - const [measuredHeight, setHeight] = useState(0); - useResizeObserver( - () => plotRef.current?.svg ?? null, - entry => fitHeight && setHeight(entry.borderBoxHeight) - ); - const height = fitHeight ? measuredHeight : explicitHeight; + const height = useHeightMeasure(() => plotRef.current?.svg ?? null, !fitHeight) ?? explicitHeight; const dimensions = useMemo( () =>