From 59f603d05d9a98ba042802c648f4fe533e1aad07 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Thu, 3 Sep 2026 11:27:30 +0100 Subject: [PATCH] Refactor ElipsisText Component for Improved State Management - Replaced refs with state variables for container and measure elements to enhance reactivity and maintainability. - Updated layout effects to utilize new state variables, ensuring accurate measurement and truncation logic. - Improved cleanup logic in useLayoutEffect to prevent memory leaks and ensure proper event handling for font loading and observers. --- .../Dashboard/common/ElipsisText.jsx | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/components/Dashboard/common/ElipsisText.jsx b/src/components/Dashboard/common/ElipsisText.jsx index 1c66ca08..40c478da 100644 --- a/src/components/Dashboard/common/ElipsisText.jsx +++ b/src/components/Dashboard/common/ElipsisText.jsx @@ -31,42 +31,60 @@ const ElipsisText = ({ children, style, className, title, code, ...rest }) => { const text = childrenToString(children) const { start, end } = splitMiddle(text) - const containerRef = useRef(null) - const measureRef = useRef(null) + const [containerEl, setContainerEl] = useState(null) + const [measureEl, setMeasureEl] = useState(null) const truncatedRef = useRef(false) const [truncated, setTruncated] = useState(false) useLayoutEffect(() => { - const container = containerRef.current - const measure = measureRef.current - if (!container || !measure) return + if (!containerEl || !measureEl) return + + let raf1 = 0 + let raf2 = 0 + let cancelled = false const update = () => { - const available = container.getBoundingClientRect().width + if (cancelled) return + + const available = containerEl.getBoundingClientRect().width if (available < 1) return - const textWidth = measure.getBoundingClientRect().width + const textWidth = measureEl.getBoundingClientRect().width const next = Boolean(end) && textWidth > available if (truncatedRef.current === next) return + truncatedRef.current = next setTruncated(next) } update() - const frame = requestAnimationFrame(update) + raf1 = requestAnimationFrame(() => { + update() + raf2 = requestAnimationFrame(update) + }) const resizeObserver = new ResizeObserver(update) - resizeObserver.observe(container) + resizeObserver.observe(containerEl) const intersectionObserver = new IntersectionObserver(update) - intersectionObserver.observe(container) + intersectionObserver.observe(containerEl) + + const onFontsReady = () => { + if (!cancelled) update() + } + const fonts = typeof document !== 'undefined' ? document.fonts : null + fonts?.ready?.then(onFontsReady) + fonts?.addEventListener?.('loadingdone', onFontsReady) return () => { - cancelAnimationFrame(frame) + cancelled = true + cancelAnimationFrame(raf1) + cancelAnimationFrame(raf2) resizeObserver.disconnect() intersectionObserver.disconnect() + fonts?.removeEventListener?.('loadingdone', onFontsReady) } - }, [text, end]) + }, [containerEl, measureEl, text, end]) const showTruncated = truncated && Boolean(end) const RootTag = code ? 'code' : 'span' @@ -74,9 +92,9 @@ const ElipsisText = ({ children, style, className, title, code, ...rest }) => { const content = ( - + {text} {text} @@ -96,7 +114,7 @@ const ElipsisText = ({ children, style, className, title, code, ...rest }) => { className={['elipsis-text-wrapper', className].filter(Boolean).join(' ')} style={style} > - {truncated ? {content} : content} + {content} ) }