From 104daa45dbcd07ec04a57b470a9bcc422726c9ae Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:52:29 +0300 Subject: [PATCH] [v11.1.x] Scrollbar: fix scrollTo being called on stale reference (#90491) Scrollbar: fix scrollTo being called on stale reference (#90346) * fix: fix scrollTo being called on stale reference (cherry picked from commit bb52c340cc9f710ecf59fbccbf48c7e1a78c8037) Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com> --- .../CustomScrollbar/CustomScrollbar.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index ab949ebc99e..2b9d694f790 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -60,11 +60,7 @@ export const CustomScrollbar = ({ } }, [ref, scrollRefCallback]); - useEffect(() => { - if (ref.current && scrollTop != null) { - ref.current.scrollTop(scrollTop); - } - }, [scrollTop]); + useScrollTop(ref.current, scrollTop); /** * Special logic for doing a update a few milliseconds after mount to check for @@ -216,3 +212,21 @@ const getStyles = (theme: GrafanaTheme2) => { }), }; }; + +/** + * Calling scrollTop on a scrollbar ref in a useEffect can race with internal state in react-custom-scrollbars-2, causing scrollTop to get called on a stale reference, which prevents the element from scrolling as desired. + * Adding the reference to the useEffect dependency array not notify react that the reference has changed (and is an eslint violation), so we create a custom hook so updates to the reference trigger another render, fixing the race condition bug. + * + * @param scrollBar + * @param scrollTop + */ +function useScrollTop( + scrollBar: (Scrollbars & { view: HTMLDivElement; update: () => void }) | null, + scrollTop?: number +) { + useEffect(() => { + if (scrollBar && scrollTop != null) { + scrollBar.scrollTop(scrollTop); + } + }, [scrollTop, scrollBar]); +}