From 903f101c4bc19ce9c56c68e7d441123bd79ad272 Mon Sep 17 00:00:00 2001 From: Oleksandr Dubenko Date: Tue, 19 Nov 2024 15:53:08 +0100 Subject: [PATCH] frontend: Use stable references and memoize Sidebar component Signed-off-by: Oleksandr Dubenko --- frontend/src/components/Sidebar/Sidebar.tsx | 323 ++++++++++---------- 1 file changed, 166 insertions(+), 157 deletions(-) diff --git a/frontend/src/components/Sidebar/Sidebar.tsx b/frontend/src/components/Sidebar/Sidebar.tsx index 965a20eace..529db8ab55 100644 --- a/frontend/src/components/Sidebar/Sidebar.tsx +++ b/frontend/src/components/Sidebar/Sidebar.tsx @@ -5,7 +5,7 @@ import Drawer from '@mui/material/Drawer'; import Grid from '@mui/material/Grid'; import List from '@mui/material/List'; import useMediaQuery from '@mui/material/useMediaQuery'; -import React from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { useHistory } from 'react-router-dom'; @@ -112,7 +112,7 @@ function SidebarToggleButton() { ); } -function DefaultLinkArea(props: { sidebarName: string; isOpen: boolean }) { +const DefaultLinkArea = memo((props: { sidebarName: string; isOpen: boolean }) => { const { sidebarName, isOpen } = props; if (sidebarName === DefaultSidebars.HOME) { @@ -151,7 +151,7 @@ function DefaultLinkArea(props: { sidebarName: string; isOpen: boolean }) { ); -} +}); export default function Sidebar() { const { t, i18n } = useTranslation(['glossary', 'translation']); @@ -186,6 +186,15 @@ export default function Sidebar() { const search = namespaces.size !== 0 ? `?namespace=${[...namespaces].join('+')}` : ''; + const handleToggleOpen = useCallback(() => { + dispatch(setWhetherSidebarOpen(!sidebar.isSidebarOpen)); + }, [sidebar.isSidebarOpen]); + + const linkArea = useMemo( + () => , + [sidebar.selected.sidebar, isOpen] + ); + if (sidebar.selected.sidebar === null || !sidebar?.isVisible) { return null; } @@ -199,10 +208,8 @@ export default function Sidebar() { isTemporaryDrawer={isTemporaryDrawer} selectedName={sidebar?.selected.item} search={search} - onToggleOpen={() => { - dispatch(setWhetherSidebarOpen(!sidebar.isSidebarOpen)); - }} - linkArea={} + onToggleOpen={handleToggleOpen} + linkArea={linkArea} /> ); } @@ -228,161 +235,163 @@ export interface PureSidebarProps { linkArea: React.ReactNode; } -export function PureSidebar({ - open, - openUserSelected, - items, - selectedName, - isTemporaryDrawer = false, - isNarrowOnly = false, - onToggleOpen, - search, - linkArea, -}: PureSidebarProps) { - const { t } = useTranslation(); - const temporarySideBarOpen = open === true && isTemporaryDrawer && openUserSelected === true; - - // The large sidebar does not open in medium view (600-960px). - const largeSideBarOpen = - (open === true && !isNarrowOnly) || (open === true && temporarySideBarOpen); - - /** - * For closing the sidebar if temporaryDrawer on mobile. - */ - const toggleDrawer = (event: React.KeyboardEvent | React.MouseEvent) => { - if ( - event.type === 'keydown' && - ((event as React.KeyboardEvent).key === 'Tab' || - (event as React.KeyboardEvent).key === 'Shift') - ) { - return; - } - onToggleOpen(); - }; - - const contents = ( - <> - ({ - ...theme.mixins.toolbar, - })} - /> - - - - {items.map(item => ( - - ))} - - - - ({ - '&, & *, & svg': { - color: theme.palette.sidebarLink.color, - }, - '& .MuiButton-root': { - color: theme.palette.sidebarButtonInLinkArea.color, - '&:hover': { - background: theme.palette.sidebarButtonInLinkArea.hover.background, +export const PureSidebar = memo( + ({ + open, + openUserSelected, + items, + selectedName, + isTemporaryDrawer = false, + isNarrowOnly = false, + onToggleOpen, + search, + linkArea, + }: PureSidebarProps) => { + const { t } = useTranslation(); + const temporarySideBarOpen = open === true && isTemporaryDrawer && openUserSelected === true; + + // The large sidebar does not open in medium view (600-960px). + const largeSideBarOpen = + (open === true && !isNarrowOnly) || (open === true && temporarySideBarOpen); + + /** + * For closing the sidebar if temporaryDrawer on mobile. + */ + const toggleDrawer = (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + onToggleOpen(); + }; + + const contents = ( + <> + ({ + ...theme.mixins.toolbar, + })} + /> + + + + {items.map(item => ( + + ))} + + + + ({ + '&, & *, & svg': { + color: theme.palette.sidebarLink.color, }, - }, - '& .MuiButton-containedPrimary': { - background: theme.palette.sidebarButtonInLinkArea.primary.background, - '&:hover': { - background: theme.palette.sidebarButtonInLinkArea.hover.background, + '& .MuiButton-root': { + color: theme.palette.sidebarButtonInLinkArea.color, + '&:hover': { + background: theme.palette.sidebarButtonInLinkArea.hover.background, + }, }, - }, - })} - > - {linkArea} - + '& .MuiButton-containedPrimary': { + background: theme.palette.sidebarButtonInLinkArea.primary.background, + '&:hover': { + background: theme.palette.sidebarButtonInLinkArea.hover.background, + }, + }, + })} + > + {linkArea} + + - - - ); + + ); - const conditionalProps = isTemporaryDrawer - ? { - open: temporarySideBarOpen, - onClose: onToggleOpen, - } - : {}; + const conditionalProps = isTemporaryDrawer + ? { + open: temporarySideBarOpen, + onClose: onToggleOpen, + } + : {}; - return ( - - { - const drawer = { - width: drawerWidth, - flexShrink: 0, - background: theme.palette.sidebarBg, - }; - - const drawerOpen = { - width: drawerWidth, - transition: theme.transitions.create('width', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.enteringScreen, - }), - background: theme.palette.sidebarBg, - }; - - const drawerClose = { - transition: theme.transitions.create('width', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - overflowX: 'hidden', - width: '56px', - [theme.breakpoints.down('xs')]: { - background: 'initial', - }, - [theme.breakpoints.down('sm')]: { - width: theme.spacing(0), - }, - [theme.breakpoints.up('sm')]: { - width: '72px', - }, - background: theme.palette.sidebarBg, - }; - - if ( - (isTemporaryDrawer && temporarySideBarOpen) || - (!isTemporaryDrawer && largeSideBarOpen) - ) { - return { ...drawer, ...drawerOpen, '& .MuiPaper-root': { ...drawerOpen } }; - } else { - return { ...drawer, ...drawerClose, '& .MuiPaper-root': { ...drawerClose } }; - } - }} - {...conditionalProps} - > - {contents} - - - ); -} + return ( + + { + const drawer = { + width: drawerWidth, + flexShrink: 0, + background: theme.palette.sidebarBg, + }; + + const drawerOpen = { + width: drawerWidth, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + background: theme.palette.sidebarBg, + }; + + const drawerClose = { + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: 'hidden', + width: '56px', + [theme.breakpoints.down('xs')]: { + background: 'initial', + }, + [theme.breakpoints.down('sm')]: { + width: theme.spacing(0), + }, + [theme.breakpoints.up('sm')]: { + width: '72px', + }, + background: theme.palette.sidebarBg, + }; + + if ( + (isTemporaryDrawer && temporarySideBarOpen) || + (!isTemporaryDrawer && largeSideBarOpen) + ) { + return { ...drawer, ...drawerOpen, '& .MuiPaper-root': { ...drawerOpen } }; + } else { + return { ...drawer, ...drawerClose, '& .MuiPaper-root': { ...drawerClose } }; + } + }} + {...conditionalProps} + > + {contents} + + + ); + } +); export function useSidebarItem( sidebarDesc: string | null | { item: string | null; sidebar?: string }