From e7c3c8609e7cdc3aba2e04debfdf739f589e8805 Mon Sep 17 00:00:00 2001 From: schroda <50052685+schroda@users.noreply.github.com> Date: Sun, 12 Jan 2025 18:57:33 +0100 Subject: [PATCH] Improve "DownloadQueue" render performance --- .../downloads/screens/DownloadQueue.tsx | 153 +++++++++--------- 1 file changed, 80 insertions(+), 73 deletions(-) diff --git a/src/modules/downloads/screens/DownloadQueue.tsx b/src/modules/downloads/screens/DownloadQueue.tsx index 6bed1361..5d97b771 100644 --- a/src/modules/downloads/screens/DownloadQueue.tsx +++ b/src/modules/downloads/screens/DownloadQueue.tsx @@ -16,7 +16,7 @@ import Stack from '@mui/material/Stack'; import Box, { BoxProps } from '@mui/material/Box'; import Tooltip from '@mui/material/Tooltip'; import IconButton from '@mui/material/IconButton'; -import React, { useContext, useEffect, useLayoutEffect } from 'react'; +import React, { memo, useCallback, useContext, useEffect, useLayoutEffect } from 'react'; import { DragDropContext, Draggable, DraggableProvided, DropResult } from 'react-beautiful-dnd'; import Typography from '@mui/material/Typography'; import { Link } from 'react-router-dom'; @@ -45,79 +45,86 @@ const HeightPreservingItem = ({ children, ...props }: BoxProps) => ( ); -const DownloadChapterItem = ({ - provided, - item, - handleDelete, - handleRetry, -}: { - provided: DraggableProvided; - item: ChapterDownloadStatus; - handleDelete: (chapter: ChapterIdInfo) => void; - handleRetry: (chapter: ChapterIdInfo) => void; -}) => { - const { t } = useTranslation(); +const DownloadChapterItem = memo( + ({ + provided, + item, + handleDelete, + handleRetry, + }: { + provided: DraggableProvided; + item: ChapterDownloadStatus; + handleDelete: (chapter: ChapterIdInfo) => void; + handleRetry: (chapter: ChapterIdInfo) => void; + }) => { + const { t } = useTranslation(); - return ( - - - - - - - - - - {item.manga.title} - - - {item.chapter.name} - - - - {item.state === DownloadState.Error && ( - + return ( + + + + + + + + + + {item.manga.title} + + + {item.chapter.name} + + + + {item.state === DownloadState.Error && ( + + { + e.preventDefault(); + e.stopPropagation(); + handleRetry(item.chapter); + }} + size="large" + > + + + + )} + { e.preventDefault(); e.stopPropagation(); - handleRetry(item.chapter); + handleDelete(item.chapter); }} size="large" > - + - )} - - { - e.preventDefault(); - e.stopPropagation(); - handleDelete(item.chapter); - }} - size="large" - > - - - - - - - - ); -}; + + + + + ); + }, +); export const DownloadQueue: React.FC = () => { const { t } = useTranslation(); @@ -213,15 +220,15 @@ export const DownloadQueue: React.FC = () => { categoryReorder(queue, result.source.index, result.destination.index); }; - const handleRetry = async (chapter: ChapterIdInfo) => { + const handleRetry = useCallback(async (chapter: ChapterIdInfo) => { try { await requestManager.addChapterToDownloadQueue(chapter.id).response; } catch (e) { makeToast(t('download.queue.error.label.failed_to_remove'), 'error', getErrorMessage(e)); } - }; + }, []); - const handleDelete = async (chapter: ChapterIdInfo) => { + const handleDelete = useCallback(async (chapter: ChapterIdInfo) => { const isRunning = status === DownloaderState.Started; try { @@ -246,7 +253,7 @@ export const DownloadQueue: React.FC = () => { } requestManager.startDownloads().response.catch(defaultPromiseErrorHandler('DownloadQueue::startDownloads')); - }; + }, []); if (isLoading) { return ; @@ -285,17 +292,17 @@ export const DownloadQueue: React.FC = () => { item.chapter.id} - itemContent={(index, item) => ( - + totalCount={queue.length} + computeItemKey={(index) => queue[index].chapter.id} + itemContent={(index) => ( + {(draggableProvided) => (