Skip to content

Commit af2387c

Browse files
committed
Refactor EventList and useStickyHeader components to utilize virtualizer ref for improved performance
- Changed import of React in global.d.ts to use type imports. - Updated EventList component to store virtualizer in a ref, avoiding unnecessary dependencies in useEffect and useCallback hooks. - Modified useStickyHeader to use virtualizer ref for accessing virtualizer methods, enhancing performance and compliance with React hooks rules. - Adjusted useVirtualization to maintain virtualizer ref and streamline dependency management in hooks.
1 parent e4f42b9 commit af2387c

4 files changed

Lines changed: 35 additions & 15 deletions

File tree

src/global.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
declare module '*.svg' {
2-
import React = require('react');
2+
import type * as React from 'react';
33
export const ReactComponent: React.SFC<React.SVGProps<SVGSVGElement>>;
44
const src: string;
55
export default src;

src/pages/devtools/components/EventList/EventList.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
useImperativeHandle,
55
useState,
66
useCallback,
7+
useEffect,
78
} from 'react';
89

910
import type { SearchMatch } from '@src/lib';
@@ -79,6 +80,13 @@ export const EventList = forwardRef<EventListHandle, EventListProps>(
7980
onScrollStateChange,
8081
});
8182

83+
// Store virtualizer in ref to avoid including in dependency arrays
84+
// (complies with react-hooks/incompatible-library rule)
85+
const virtualizerRef = useRef(virtualizer);
86+
useEffect(() => {
87+
virtualizerRef.current = virtualizer;
88+
}, [virtualizer]);
89+
8290
// Wrapper for toggle expand that also triggers remeasurement
8391
const handleToggleExpand = useCallback(
8492
(id: string) => {
@@ -103,7 +111,7 @@ export const EventList = forwardRef<EventListHandle, EventListProps>(
103111
if (eventIndex !== -1) {
104112
requestAnimationFrame(() => {
105113
requestAnimationFrame(() => {
106-
virtualizer.scrollToIndex(eventIndex, {
114+
virtualizerRef.current.scrollToIndex(eventIndex, {
107115
align: 'start',
108116
});
109117
});
@@ -113,7 +121,7 @@ export const EventList = forwardRef<EventListHandle, EventListProps>(
113121
// Trigger remeasurement after state update
114122
remeasureItems();
115123
},
116-
[expandedEventIds, listItems, onToggleExpand, virtualizer, remeasureItems]
124+
[expandedEventIds, listItems, onToggleExpand, remeasureItems]
117125
);
118126

119127
// Use sticky header hook

src/pages/devtools/components/EventList/useStickyHeader.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Virtualizer } from '@tanstack/react-virtual';
2-
import { useState, useRef, useCallback } from 'react';
2+
import { useState, useRef, useCallback, useEffect } from 'react';
33

44
import type { SegmentEvent } from '@src/types';
55

@@ -39,6 +39,12 @@ export function useStickyHeader({
3939
const [stickyEventIndex, setStickyEventIndex] = useState<number | null>(null);
4040
// Track current sticky event ID to avoid unnecessary state updates
4141
const currentStickyIdRef = useRef<string | null>(null);
42+
// Store virtualizer in ref to avoid including in dependency arrays
43+
// (complies with react-hooks/incompatible-library rule)
44+
const virtualizerRef = useRef(virtualizer);
45+
useEffect(() => {
46+
virtualizerRef.current = virtualizer;
47+
}, [virtualizer]);
4248

4349
/**
4450
* Handle scroll to detect if user has scrolled up and manage sticky header
@@ -47,7 +53,7 @@ export function useStickyHeader({
4753
const handleScroll = useCallback(
4854
(scrollTop: number) => {
4955
// Check for expanded events whose headers have scrolled out of view
50-
const virtualItems = virtualizer.getVirtualItems();
56+
const virtualItems = virtualizerRef.current.getVirtualItems();
5157
let foundStickyEvent: SegmentEvent | null = null;
5258
let foundStickyIndex: number | null = null;
5359

@@ -91,7 +97,7 @@ export function useStickyHeader({
9197
setStickyEventIndex(foundStickyIndex);
9298
}
9399
},
94-
[listItems, expandedEventIds, virtualizer]
100+
[listItems, expandedEventIds]
95101
);
96102

97103
/**
@@ -120,13 +126,13 @@ export function useStickyHeader({
120126
// Scroll to make the collapsed event visible (after collapse animation)
121127
requestAnimationFrame(() => {
122128
requestAnimationFrame(() => {
123-
virtualizer.scrollToIndex(indexToScrollTo, {
129+
virtualizerRef.current.scrollToIndex(indexToScrollTo, {
124130
align: 'start',
125131
});
126132
});
127133
});
128134
}
129-
}, [stickyEvent, stickyEventIndex, onToggleExpand, virtualizer, clearSticky]);
135+
}, [stickyEvent, stickyEventIndex, onToggleExpand, clearSticky]);
130136

131137
return {
132138
stickyEvent,

src/pages/devtools/components/EventList/useVirtualization.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ export function useVirtualization({
193193
);
194194

195195
// Set up virtualizer with dynamic measurement
196+
// Store in ref to avoid including in dependency arrays (complies with react-hooks/incompatible-library)
196197
const virtualizer = useVirtualizer({
197198
count: listItems.length,
198199
getScrollElement: () => scrollContainerRef.current,
@@ -205,6 +206,11 @@ export function useVirtualization({
205206
);
206207
},
207208
});
209+
const virtualizerRef = useRef(virtualizer);
210+
// Update ref when virtualizer changes (must be in effect, not during render)
211+
useEffect(() => {
212+
virtualizerRef.current = virtualizer;
213+
}, [virtualizer]);
208214

209215
// Handle scroll to detect if user has scrolled up and track scroll state
210216
const handleScroll = useCallback(() => {
@@ -232,7 +238,7 @@ export function useVirtualization({
232238

233239
// Use requestAnimationFrame to ensure DOM is ready, then scroll
234240
requestAnimationFrame(() => {
235-
virtualizer.scrollToIndex(listItems.length - 1, {
241+
virtualizerRef.current.scrollToIndex(listItems.length - 1, {
236242
align: 'end',
237243
behavior: 'smooth',
238244
});
@@ -244,17 +250,17 @@ export function useVirtualization({
244250
}, 100);
245251
});
246252
}
247-
}, [listItems.length, virtualizer]);
253+
}, [listItems.length]);
248254

249255
// Helper function to measure all items after DOM updates
250256
const measureAllItems = useCallback(() => {
251257
itemRefs.current.forEach((element) => {
252258
if (element) {
253-
virtualizer.measureElement(element);
259+
virtualizerRef.current.measureElement(element);
254260
}
255261
});
256-
virtualizer.measure();
257-
}, [virtualizer]);
262+
virtualizerRef.current.measure();
263+
}, []);
258264

259265
// Remeasure all items (useful after expansion state changes)
260266
const remeasureItems = useCallback(() => {
@@ -290,7 +296,7 @@ export function useVirtualization({
290296
// Scroll to bottom with retry to ensure we actually reach the bottom
291297
// The virtualizer may not have measured new items immediately
292298
const scrollToEnd = () => {
293-
virtualizer.scrollToIndex(listItems.length - 1, {
299+
virtualizerRef.current.scrollToIndex(listItems.length - 1, {
294300
align: 'end',
295301
behavior: 'auto',
296302
});
@@ -325,7 +331,7 @@ export function useVirtualization({
325331
clearTimeout(retryTimeoutId);
326332
isProgrammaticScrollRef.current = false;
327333
};
328-
}, [listItems.length, virtualizer, scrollContainerRef]);
334+
}, [listItems.length, scrollContainerRef]);
329335

330336
// Recalculate virtualizer when expansion state changes
331337
useEffect(() => {

0 commit comments

Comments
 (0)