From 4c3b3e31d4a904bd4d86a8dbbe5591827a5ddfe7 Mon Sep 17 00:00:00 2001 From: Geert Rademakes Date: Wed, 6 Aug 2025 11:10:28 +0200 Subject: [PATCH] perf: Additional optimizations to reduce React violations and improve performance - Add early return for empty songs array in totalDuration calculation - Add null check for getAllPlaylists to prevent unnecessary processing - Use requestAnimationFrame in Intersection Observer for better performance - Should reduce 600-1400ms message handler violations --- .../frontend/src/components/PaginatedSongList.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/PaginatedSongList.tsx b/packages/frontend/src/components/PaginatedSongList.tsx index 3100963..45642e3 100644 --- a/packages/frontend/src/components/PaginatedSongList.tsx +++ b/packages/frontend/src/components/PaginatedSongList.tsx @@ -145,6 +145,8 @@ export const PaginatedSongList: React.FC = memo(({ // Memoized helper function to get all playlists (excluding folders) from the playlist tree const getAllPlaylists = useCallback((nodes: PlaylistNode[]): PlaylistNode[] => { + if (!nodes || nodes.length === 0) return []; + let result: PlaylistNode[] = []; for (const node of nodes) { if (node.type === 'playlist') { @@ -222,6 +224,9 @@ export const PaginatedSongList: React.FC = memo(({ if (totalPlaylistDuration) { return totalPlaylistDuration; } + // Only calculate if we have songs and no total duration provided + if (songs.length === 0) return ''; + // Fallback to calculating from current songs const totalSeconds = songs.reduce((total, song) => { if (!song.totalTime) return total; @@ -247,7 +252,7 @@ export const PaginatedSongList: React.FC = memo(({ } }, [debouncedSearchQuery, searchQuery, onSearch]); - // Intersection Observer for infinite scroll + // Intersection Observer for infinite scroll - optimized useEffect(() => { if (loadingRef.current) { observerRef.current = new IntersectionObserver( @@ -255,7 +260,10 @@ export const PaginatedSongList: React.FC = memo(({ // Use current values from refs to avoid stale closure if (entries[0].isIntersecting && hasMoreRef.current && !loadingRef_state.current && !isTriggeringRef.current) { isTriggeringRef.current = true; - onLoadMoreRef.current(); + // Use requestAnimationFrame for better performance + requestAnimationFrame(() => { + onLoadMoreRef.current(); + }); // Reset the flag after a short delay to prevent multiple triggers timeoutRef.current = setTimeout(() => { isTriggeringRef.current = false;