Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 524e68bd3a fix: trigger prefetch earlier in virtualized table scroll handler
https://sonarly.com/issue/17183?type=bug

The virtualized record table's scroll-triggered data fetching has insufficient prefetching — the initial load covers only 60 records (6 pages), but the overscan window and debounce logic delay subsequent page fetches, causing visible skeleton placeholders during slow scrolling.

Fix: Two changes in `RecordTableVirtualizedRowTreadmillEffect.tsx`:

**1. Fixed the scroll speed dead zone (50-200 rows/s) where no data fetch was triggered**

Previously, `triggerFetchPages()` was only called inside the `else if (speed < 50 rows/s)` branch. For scroll speeds between 50-200 rows/s (typical slow-to-medium scrolling), neither the high-speed nor low-speed branch would execute, meaning no data fetch was triggered during active scrolling. The only fallback was `handleAfterLastScrollDebounced` which required the user to stop scrolling for 300ms.

The fix moves `triggerFetchPages()` outside the speed threshold branches so it fires on every scroll event regardless of speed. This is safe because:
- `triggerFetchPages` itself is debounced (25ms with 2000ms maxWait)
- `triggerFetchPagesWithoutDebounce` has an early return when `lowDetailsActivated` is true
- Already-loaded pages are filtered out before any network request

The speed thresholds now only control the low-details rendering mode (showing skeletons vs full cells during fast scrolling), which was their original intent.

**2. Reduced `LAST_SCROLL_DEBOUNCE_TIME` from 300ms to 100ms**

This is the debounce delay for `handleAfterLastScroll`, which fires after the user stops scrolling. 300ms felt sluggish — the user would see placeholders for a noticeable period after stopping. 100ms is fast enough to feel responsive while still batching rapid scroll-stop events.
2026-03-21 12:38:02 +00:00
@@ -20,7 +20,7 @@ export const TIME_BEFORE_DEACTIVATING_LOW_DETAILS = 20;
export const NUMBER_OF_EVENTS_TO_COMPUTE_AVERAGE = 10;
const TIME_BETWEEN_TWO_SCROLL_HANDLING = 20;
const LAST_SCROLL_DEBOUNCE_TIME = 300;
const LAST_SCROLL_DEBOUNCE_TIME = 100;
export const RecordTableVirtualizedRowTreadmillEffect = () => {
const { scrollWrapperHTMLElement } = useScrollWrapperHTMLElement();
@@ -195,12 +195,13 @@ export const RecordTableVirtualizedRowTreadmillEffect = () => {
SCROLL_SPEED_THRESHOLD_IN_ROWS_PER_SECOND_TO_DEACTIVATE_LOW_DETAILS
) {
deactivateLowDetailsDebounced();
triggerFetchPages();
}
} else {
triggerFetchPages();
}
// Always trigger fetch during scrolling so data is prefetched
// before the user reaches unloaded rows. The fetch itself is
// debounced and skips when lowDetailsActivated is true.
triggerFetchPages();
},
[
lastScrollMeasurementsCallbackState,