From ea194f126ce19ac18992775462d9d4ade0cea1a5 Mon Sep 17 00:00:00 2001 From: Andrey Sychev Date: Thu, 14 Nov 2024 16:59:57 +0100 Subject: [PATCH] CB-5703 refactor: optimize data grid onScroll handler handleScroll in DataGrid entails small performance degradation due to el.scrollTop, el.clientHeight, el.scrollHeight calls and hence reflow of layout, more on that here: https://gist.github.com/paulirish/5d52fb081b3570c81e3a/565c05680b27c9cfd9f5e971d295cd558c3e1843 What is done: 1. throttle added to not fire handleScroll on every render, because the main goal of that function is to detect when a user approaches the end of a page = reduce number of calls 2. Inside the handler the code was refactored in a way that we get scrollTop only once to calculate toBottom and inverse the clause logic and replace early return (!A || !B) with a clause with (A && B) logic to not check B on every call which is also triggering reflow. --- .../src/DataGrid/DataGridTable.tsx | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx index 07057d8a7c..003c8672aa 100644 --- a/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx +++ b/webapp/packages/plugin-data-spreadsheet-new/src/DataGrid/DataGridTable.tsx @@ -13,6 +13,7 @@ import { useService } from '@cloudbeaver/core-di'; import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events'; import { Executor } from '@cloudbeaver/core-executor'; import { ClipboardService } from '@cloudbeaver/core-ui'; +import { throttle } from '@cloudbeaver/core-utils'; import { useCaptureViewContext } from '@cloudbeaver/core-view'; import { type CellSelectArgs, DataGrid, type DataGridHandle, type Position } from '@cloudbeaver/plugin-data-grid'; import { @@ -52,8 +53,8 @@ interface IInnerState { } function isAtBottom(event: React.UIEvent): boolean { - const target = event.target as HTMLDivElement; - return target.clientHeight + target.scrollTop + 100 > target.scrollHeight; + const { clientHeight, scrollTop, scrollHeight } = event.target as HTMLDivElement; + return clientHeight + scrollTop + 100 > scrollHeight; } const rowHeight = 25; @@ -410,23 +411,21 @@ export const DataGridTable = observer(function DataGridT }; const handleScroll = useCallback( - async (event: React.UIEvent) => { - const target = event.target as HTMLDivElement; - const toBottom = target.scrollTop > innerState.lastScrollTop; + throttle(async (event: React.UIEvent) => { + const scrollTop = (event.target as HTMLDivElement).scrollTop; + const toBottom = scrollTop > innerState.lastScrollTop; - innerState.lastScrollTop = target.scrollTop; + innerState.lastScrollTop = scrollTop; - if (!toBottom || !isAtBottom(event)) { - return; - } + if (toBottom && isAtBottom(event)) { + const result = model.source.getResult(resultIndex); + if (result?.loadedFully) { + return; + } - const result = model.source.getResult(resultIndex); - if (result?.loadedFully) { - return; + await model.requestDataPortion(0, model.countGain + model.source.count); } - - await model.requestDataPortion(0, model.countGain + model.source.count); - }, + }, 200), [model, resultIndex], );