perf: optimize ThemePlaylists component by memoizing handlers, derived state, and stack objects
All checks were successful
Deploy to production / build (push) Successful in 21s
All checks were successful
Deploy to production / build (push) Successful in 21s
This commit is contained in:
@@ -2,7 +2,7 @@ import { Box, Group, LoadingOverlay, SimpleGrid, Stack, Text } from '@mantine/co
|
|||||||
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
|
||||||
import { NoItemsFound, SettingsHeader } from 'components';
|
import { NoItemsFound, SettingsHeader } from 'components';
|
||||||
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState, useMemo, useCallback } from 'react';
|
||||||
import { FiChevronRight } from 'react-icons/fi';
|
import { FiChevronRight } from 'react-icons/fi';
|
||||||
import { textClasses } from 'styles';
|
import { textClasses } from 'styles';
|
||||||
import type { Theme, ThemePlaylist } from 'types';
|
import type { Theme, ThemePlaylist } from 'types';
|
||||||
@@ -21,13 +21,15 @@ export function ThemePlaylists() {
|
|||||||
});
|
});
|
||||||
const [searchValue, setSearchValue] = useState<string | number>('');
|
const [searchValue, setSearchValue] = useState<string | number>('');
|
||||||
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
|
||||||
const themePlaylists: ThemePlaylist[] =
|
|
||||||
themePlaylistData?.filter(
|
const themePlaylists: ThemePlaylist[] = useMemo(() => {
|
||||||
|
return themePlaylistData?.filter(
|
||||||
(a: ThemePlaylist) =>
|
(a: ThemePlaylist) =>
|
||||||
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()) ||
|
||||||
a.name.includes('Time of Day'),
|
a.name.includes('Time of Day'),
|
||||||
) || [];
|
) || [];
|
||||||
const defaultPlaylist = {
|
}, [themePlaylistData, debouncedSearch]);
|
||||||
|
const defaultPlaylist: ThemePlaylist = {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
default: 0,
|
default: 0,
|
||||||
name: 'New Playlist',
|
name: 'New Playlist',
|
||||||
@@ -40,30 +42,73 @@ export function ThemePlaylists() {
|
|||||||
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
const { currentThemePlaylist, theme, isMobile } = useCommonHooks();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handleOpen = (playlist: ThemePlaylist) => {
|
const handleOpen = useCallback((playlist: ThemePlaylist) => {
|
||||||
setSelectedPlaylist(playlist);
|
setSelectedPlaylist(playlist);
|
||||||
open();
|
open();
|
||||||
};
|
}, [open]);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = useCallback(() => {
|
||||||
setSelectedPlaylist(defaultPlaylist);
|
setSelectedPlaylist(defaultPlaylist);
|
||||||
close();
|
close();
|
||||||
};
|
}, [close]);
|
||||||
|
|
||||||
const handleSavePlaylist = () => {
|
const handleSavePlaylist = useCallback(() => {
|
||||||
mutateAsync({
|
mutateAsync({
|
||||||
method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH',
|
method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH',
|
||||||
endpoint: themePlaylistsEndpoint,
|
endpoint: themePlaylistsEndpoint,
|
||||||
body: selectedPlaylist ?? {},
|
body: selectedPlaylist ?? {},
|
||||||
});
|
});
|
||||||
close();
|
close();
|
||||||
};
|
}, [selectedPlaylist, mutateAsync, themePlaylistsEndpoint, close]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (debouncedSearch === searchValue) {
|
if (debouncedSearch === searchValue) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [debouncedSearch]);
|
}, [debouncedSearch, searchValue]);
|
||||||
|
|
||||||
|
const allThemesIds = useMemo(() =>
|
||||||
|
allThemeData?.map((a: Theme) => a.id) ?? [],
|
||||||
|
[allThemeData]);
|
||||||
|
|
||||||
|
const backgroundsThemes = useMemo(() =>
|
||||||
|
allThemeData?.filter((a: any) => !a.backgroundImage.toLowerCase().includes('none')) ?? [],
|
||||||
|
[allThemeData]);
|
||||||
|
|
||||||
|
const noBackgroundsThemes = useMemo(() =>
|
||||||
|
allThemeData?.filter((a: any) => a.backgroundImage.toLowerCase().includes('none')) ?? [],
|
||||||
|
[allThemeData]);
|
||||||
|
|
||||||
|
const timeOfDayPlaylist = useMemo(() => {
|
||||||
|
const found = themePlaylists?.find((a: ThemePlaylist) => a.name === 'Time of Day');
|
||||||
|
return {
|
||||||
|
id: found?.id,
|
||||||
|
name: 'Time of Day',
|
||||||
|
default: 1,
|
||||||
|
themeList: found?.themeList ?? '[]',
|
||||||
|
};
|
||||||
|
}, [themePlaylists]);
|
||||||
|
|
||||||
|
const allStack = useMemo(() => ({
|
||||||
|
id: undefined,
|
||||||
|
name: 'All',
|
||||||
|
default: 1,
|
||||||
|
themeList: `[${allThemesIds.join(',')}]`,
|
||||||
|
}), [allThemesIds]);
|
||||||
|
|
||||||
|
const backgroundsStack = useMemo(() => ({
|
||||||
|
id: undefined,
|
||||||
|
name: 'Backgrounds',
|
||||||
|
default: 1,
|
||||||
|
themeList: `[${backgroundsThemes.map((a: Theme) => a.id).join(',')}]`,
|
||||||
|
}), [backgroundsThemes]);
|
||||||
|
|
||||||
|
const noBackgroundsStack = useMemo(() => ({
|
||||||
|
id: undefined,
|
||||||
|
name: 'No Backgrounds',
|
||||||
|
default: 1,
|
||||||
|
themeList: `[${noBackgroundsThemes.map((a: Theme) => a.id).join(',')}]`,
|
||||||
|
}), [noBackgroundsThemes]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
@@ -135,17 +180,13 @@ export function ThemePlaylists() {
|
|||||||
)}
|
)}
|
||||||
<Box>
|
<Box>
|
||||||
<ThemePlaylistStack
|
<ThemePlaylistStack
|
||||||
cardPlaylist={{
|
cardPlaylist={allStack}
|
||||||
id: undefined,
|
|
||||||
name: 'All',
|
|
||||||
default: 1,
|
|
||||||
themeList: `[${allThemeData?.map((a: Theme) => a.id) ?? ''}]`,
|
|
||||||
}}
|
|
||||||
handleOpen={handleOpen}
|
handleOpen={handleOpen}
|
||||||
allThemes={allThemeData}
|
allThemes={allThemeData}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Group>
|
</Group>
|
||||||
|
</Group>
|
||||||
<Group pos={'relative'}>
|
<Group pos={'relative'}>
|
||||||
{'Backgrounds' === currentThemePlaylist && (
|
{'Backgrounds' === currentThemePlaylist && (
|
||||||
<FiChevronRight
|
<FiChevronRight
|
||||||
@@ -166,12 +207,7 @@ export function ThemePlaylists() {
|
|||||||
)}
|
)}
|
||||||
<Box>
|
<Box>
|
||||||
<ThemePlaylistStack
|
<ThemePlaylistStack
|
||||||
cardPlaylist={{
|
cardPlaylist={backgroundsStack}
|
||||||
id: undefined,
|
|
||||||
name: 'Backgrounds',
|
|
||||||
default: 1,
|
|
||||||
themeList: `[${allThemeData?.filter((a: any) => !a.backgroundImage.toLowerCase().includes('none')).map((a: Theme) => a.id) ?? ''}]`,
|
|
||||||
}}
|
|
||||||
handleOpen={handleOpen}
|
handleOpen={handleOpen}
|
||||||
allThemes={allThemeData}
|
allThemes={allThemeData}
|
||||||
/>
|
/>
|
||||||
@@ -197,12 +233,7 @@ export function ThemePlaylists() {
|
|||||||
)}
|
)}
|
||||||
<Box>
|
<Box>
|
||||||
<ThemePlaylistStack
|
<ThemePlaylistStack
|
||||||
cardPlaylist={{
|
cardPlaylist={noBackgroundsStack}
|
||||||
id: undefined,
|
|
||||||
name: 'No Backgrounds',
|
|
||||||
default: 1,
|
|
||||||
themeList: `[${allThemeData?.filter((a: any) => a.backgroundImage.toLowerCase().includes('none')).map((a: Theme) => a.id) ?? ''}]`,
|
|
||||||
}}
|
|
||||||
handleOpen={handleOpen}
|
handleOpen={handleOpen}
|
||||||
allThemes={allThemeData}
|
allThemes={allThemeData}
|
||||||
/>
|
/>
|
||||||
@@ -228,24 +259,12 @@ export function ThemePlaylists() {
|
|||||||
)}
|
)}
|
||||||
<Box>
|
<Box>
|
||||||
<ThemePlaylistStack
|
<ThemePlaylistStack
|
||||||
cardPlaylist={{
|
cardPlaylist={timeOfDayPlaylist}
|
||||||
id:
|
|
||||||
themePlaylists?.find(
|
|
||||||
(a: ThemePlaylist) => a.name === 'Time of Day',
|
|
||||||
)?.id ?? undefined,
|
|
||||||
name: 'Time of Day',
|
|
||||||
default: 1,
|
|
||||||
themeList:
|
|
||||||
themePlaylists?.find(
|
|
||||||
(a: ThemePlaylist) => a.name === 'Time of Day',
|
|
||||||
)?.themeList ?? '[]',
|
|
||||||
}}
|
|
||||||
handleOpen={handleOpen}
|
handleOpen={handleOpen}
|
||||||
allThemes={allThemeData}
|
allThemes={allThemeData}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
|
||||||
<Group justify='center' align='center' pos={'relative'}>
|
<Group justify='center' align='center' pos={'relative'}>
|
||||||
<Text
|
<Text
|
||||||
p={0}
|
p={0}
|
||||||
|
|||||||
Reference in New Issue
Block a user