From cede148d4609a9838b6efad9ab9ba26c91af0636 Mon Sep 17 00:00:00 2001 From: Diego Medina Date: Wed, 11 May 2022 03:37:43 -0400 Subject: [PATCH] fix: native filter truncation rerendering loop on hover (#20021) --- .../nativeFilters/FilterCard/useTruncation.ts | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/useTruncation.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/useTruncation.ts index 4f2e172369..a4a893463f 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/useTruncation.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/useTruncation.ts @@ -16,18 +16,47 @@ * specific language governing permissions and limitations * under the License. */ -import { RefObject, useLayoutEffect, useState } from 'react'; +import { RefObject, useLayoutEffect, useState, useRef } from 'react'; export const useTruncation = (elementRef: RefObject) => { const [elementsTruncated, setElementsTruncated] = useState(0); const [hasHiddenElements, setHasHiddenElements] = useState(false); + const previousEffectInfoRef = useRef({ + scrollWidth: 0, + parentElementWidth: 0, + }); + useLayoutEffect(() => { const currentElement = elementRef.current; if (!currentElement) { return; } + const { scrollWidth, clientWidth, childNodes } = currentElement; + + // By using the result of this effect to truncate content + // we're effectively changing it's size. + // That will trigger another pass at this effect. + // Depending on the content elements width, that second rerender could + // yield a different truncate count, thus potentially leading to a + // rendering loop. + // There's only a need to recompute if the parent width or the width of + // the child nodes changes. + const previousEffectInfo = previousEffectInfoRef.current; + const parentElementWidth = currentElement.parentElement?.clientWidth || 0; + previousEffectInfoRef.current = { + scrollWidth, + parentElementWidth, + }; + + if ( + previousEffectInfo.parentElementWidth === parentElementWidth && + previousEffectInfo.scrollWidth === scrollWidth + ) { + return; + } + if (scrollWidth > clientWidth) { // "..." is around 6px wide const maxWidth = clientWidth - 6;