From 770c6065619478b60658a7103b7e39f1093abddf Mon Sep 17 00:00:00 2001 From: Geert Rademakes Date: Wed, 6 Aug 2025 10:59:16 +0200 Subject: [PATCH] perf: Aggressive optimization for playlist switching to make it snappy - Add React.startTransition to batch state updates in usePaginatedSongs - Optimize playlist selection handler with immediate navigation - Memoize click handlers in PlaylistItem to prevent recreation - Use replace navigation to avoid history stack delays - Should dramatically reduce 600+ms playlist switching delay --- packages/frontend/src/App.tsx | 12 ++++++---- .../src/components/PlaylistManager.tsx | 13 +++++++++-- .../frontend/src/hooks/usePaginatedSongs.ts | 23 +++++++++++-------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 80d6dd8..0ddf15b 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,6 +1,6 @@ import { Box, Button, Flex, Heading, Spinner, Text, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalFooter, VStack } from "@chakra-ui/react"; import { ChevronLeftIcon, ChevronRightIcon, SettingsIcon } from "@chakra-ui/icons"; -import { useState, useRef, useEffect, useCallback } from "react"; +import React, { useState, useRef, useEffect, useCallback } from "react"; import { useNavigate, useLocation, Routes, Route } from "react-router-dom"; import { PaginatedSongList } from "./components/PaginatedSongList"; import { PlaylistManager } from "./components/PlaylistManager"; @@ -154,13 +154,15 @@ export default function RekordboxReader() { }, [currentPlaylist, playlists, navigate, xmlLoading]); const handlePlaylistSelect = (name: string) => { - setSelectedSong(null); // Clear selected song when changing playlists + // Clear selected song immediately to prevent stale state + setSelectedSong(null); + + // Navigate immediately without any delays if (name === "All Songs") { - navigate("/"); + navigate("/", { replace: true }); } else { - // Use encodeURIComponent to properly handle spaces and special characters const encodedName = encodeURIComponent(name); - navigate(`/playlists/${encodedName}`); + navigate(`/playlists/${encodedName}`, { replace: true }); } }; diff --git a/packages/frontend/src/components/PlaylistManager.tsx b/packages/frontend/src/components/PlaylistManager.tsx index fd58e72..413bfad 100644 --- a/packages/frontend/src/components/PlaylistManager.tsx +++ b/packages/frontend/src/components/PlaylistManager.tsx @@ -101,6 +101,15 @@ const PlaylistItem: React.FC = React.memo(({ allFolders, }) => { const [isOpen, setIsOpen] = useState(false); + + // Memoize click handlers to prevent recreation + const handlePlaylistClick = useCallback(() => { + onPlaylistSelect(node.name); + }, [onPlaylistSelect, node.name]); + + const handleFolderToggle = useCallback(() => { + setIsOpen(prev => !prev); + }, []); const indent = level * 10; // Reverted back to 10px per level if (node.type === 'folder') { @@ -110,7 +119,7 @@ const PlaylistItem: React.FC = React.memo(({