From c17bd2170b3fc7f8571d59e49d43681fd9e1bd59 Mon Sep 17 00:00:00 2001 From: Slizhevsky Vladislav Date: Thu, 20 Aug 2026 11:50:28 +0200 Subject: [PATCH 1/3] [UIK-5777][core] prevent calling redundat layout/rerender calls for useScrollBarWidth hook --- .../core/src/utils/use/useScrollBarWidth.ts | 83 ++++++++++++------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/semcore/core/src/utils/use/useScrollBarWidth.ts b/semcore/core/src/utils/use/useScrollBarWidth.ts index f7a2008763..3a0cbf66bf 100644 --- a/semcore/core/src/utils/use/useScrollBarWidth.ts +++ b/semcore/core/src/utils/use/useScrollBarWidth.ts @@ -1,39 +1,66 @@ -import { useEffect, useState, useRef } from 'react'; +import { useSyncExternalStore } from 'react'; -export function useScrollBarWidth(vertical = true): number { - const [scrollBarWidth, setScrollBarWidth] = useState(0); - const af = useRef(null); +type Dimension = 'vertical' | 'horizontal'; + +const state: Record = { vertical: 0, horizontal: 0 }; +const listeners = new Set<() => void>(); + +let rafId: number | null = null; +let inited = false; + +function measureAndNotifyIfNeeded() { + if (!window.visualViewport) return; + + const nextVertical = window.innerWidth - window.visualViewport.width; + const nextHorizontal = window.innerHeight - window.visualViewport.height; - useEffect(() => { - const calculateScrollBar = () => { - if (!window.visualViewport) return; + if (nextVertical === state.vertical && nextHorizontal === state.horizontal) return; - if (!vertical) { - setScrollBarWidth(window.innerHeight - window.visualViewport.height); - return; - } + state.vertical = nextVertical; + state.horizontal = nextHorizontal; - setScrollBarWidth(window.innerWidth - window.visualViewport.width); - }; + listeners.forEach((l) => l()); +} + +function handleResize() { + if (rafId !== null) return; + + rafId = requestAnimationFrame(() => { + measureAndNotifyIfNeeded(); + rafId = null; + }); +} - const handleResize = () => { - // to handle resize 1 time per frame - if (af.current !== null) return; +function init() { + if (inited) return; - af.current = requestAnimationFrame(() => { - calculateScrollBar(); - af.current = null; - }); - }; + inited = true; - calculateScrollBar(); - window.addEventListener('resize', handleResize); + measureAndNotifyIfNeeded(); - return () => { + window.addEventListener('resize', handleResize); +} + +function subscribe(onStoreChange: () => void) { + init(); + + listeners.add(onStoreChange); + + return () => { + listeners.delete(onStoreChange); + + if (listeners.size === 0) { window.removeEventListener('resize', handleResize); - if (af.current !== null) cancelAnimationFrame(af.current); - }; - }, []); + } + }; +} + +export function useScrollBarWidth(vertical = true): number { + const dim: Dimension = vertical ? 'vertical' : 'horizontal'; - return scrollBarWidth; + return useSyncExternalStore( + subscribe, + () => state[dim], + () => 0, + ); } From 89516aecfe258c236da9035392b3b9a9535e4cc8 Mon Sep 17 00:00:00 2001 From: Slizhevsky Vladislav Date: Thu, 20 Aug 2026 13:01:41 +0200 Subject: [PATCH 2/3] [UIK-5777][core] prevent calling redundat layout/rerender calls for useScrollBarWidth hook --- semcore/core/src/utils/use/useScrollBarWidth.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/semcore/core/src/utils/use/useScrollBarWidth.ts b/semcore/core/src/utils/use/useScrollBarWidth.ts index 3a0cbf66bf..e1eee52d0f 100644 --- a/semcore/core/src/utils/use/useScrollBarWidth.ts +++ b/semcore/core/src/utils/use/useScrollBarWidth.ts @@ -8,7 +8,7 @@ const listeners = new Set<() => void>(); let rafId: number | null = null; let inited = false; -function measureAndNotifyIfNeeded() { +function measure() { if (!window.visualViewport) return; const nextVertical = window.innerWidth - window.visualViewport.width; @@ -26,7 +26,7 @@ function handleResize() { if (rafId !== null) return; rafId = requestAnimationFrame(() => { - measureAndNotifyIfNeeded(); + measure(); rafId = null; }); } @@ -36,7 +36,7 @@ function init() { inited = true; - measureAndNotifyIfNeeded(); + measure(); window.addEventListener('resize', handleResize); } From 735107e130fcfe6ff7b2def2607ea8d029a9e33f Mon Sep 17 00:00:00 2001 From: Slizhevsky Vladislav Date: Tue, 25 Aug 2026 15:46:10 +0200 Subject: [PATCH 3/3] [UIK-5777][core] prevent calling redundat layout/rerender calls for useScrollBarWidth hook --- .../core/src/utils/use/useScrollBarWidth.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/semcore/core/src/utils/use/useScrollBarWidth.ts b/semcore/core/src/utils/use/useScrollBarWidth.ts index e1eee52d0f..af51bb5135 100644 --- a/semcore/core/src/utils/use/useScrollBarWidth.ts +++ b/semcore/core/src/utils/use/useScrollBarWidth.ts @@ -1,4 +1,4 @@ -import { useSyncExternalStore } from 'react'; +import { useEffect, useRef, useState } from 'react'; type Dimension = 'vertical' | 'horizontal'; @@ -42,10 +42,10 @@ function init() { } function subscribe(onStoreChange: () => void) { - init(); - listeners.add(onStoreChange); + init(); + return () => { listeners.delete(onStoreChange); @@ -57,10 +57,18 @@ function subscribe(onStoreChange: () => void) { export function useScrollBarWidth(vertical = true): number { const dim: Dimension = vertical ? 'vertical' : 'horizontal'; + const [value, setValue] = useState(() => state[dim]); + const dimRef = useRef(dim); + + dimRef.current = dim; + + useEffect(() => { + const onChange = () => setValue(state[dimRef.current]); + + onChange(); + + return subscribe(onChange); + }, []); - return useSyncExternalStore( - subscribe, - () => state[dim], - () => 0, - ); + return value; }