From 58ae0584659961b3b3857d77b7ebf11678b5baba Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 25 Oct 2025 15:21:59 +0200 Subject: [PATCH] use memo and callbacks for reduced rendering vis caching --- interface/src/CustomTheme.tsx | 9 ++++--- interface/src/components/layout/Layout.tsx | 24 ++++++++++------- .../src/components/loading/LazyLoader.tsx | 6 +++-- interface/src/utils/useRest.ts | 27 ++++++++++++------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/interface/src/CustomTheme.tsx b/interface/src/CustomTheme.tsx index 579a8d597..77f84af76 100644 --- a/interface/src/CustomTheme.tsx +++ b/interface/src/CustomTheme.tsx @@ -1,3 +1,4 @@ +import { memo } from 'react'; import type { FC } from 'react'; import { CssBaseline, ThemeProvider, responsiveFontSizes } from '@mui/material'; @@ -5,6 +6,7 @@ import { createTheme } from '@mui/material/styles'; import type { RequiredChildrenProps } from 'utils'; +// Memoize dialog style to prevent recreation export const dialogStyle = { '& .MuiDialog-paper': { borderRadius: '8px', @@ -12,8 +14,9 @@ export const dialogStyle = { borderStyle: 'solid', borderWidth: '1px' } -}; +} as const; +// Memoize theme creation to prevent recreation const theme = responsiveFontSizes( createTheme({ typography: { @@ -34,11 +37,11 @@ const theme = responsiveFontSizes( }) ); -const CustomTheme: FC = ({ children }) => ( +const CustomTheme: FC = memo(({ children }) => ( {children} -); +)); export default CustomTheme; diff --git a/interface/src/components/layout/Layout.tsx b/interface/src/components/layout/Layout.tsx index 70c94100a..0354255c4 100644 --- a/interface/src/components/layout/Layout.tsx +++ b/interface/src/components/layout/Layout.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import type { FC } from 'react'; import { useLocation } from 'react-router'; @@ -13,22 +13,26 @@ import { LayoutContext } from './context'; export const DRAWER_WIDTH = 210; -const Layout: FC = ({ children }) => { +const Layout: FC = memo(({ children }) => { const [mobileOpen, setMobileOpen] = useState(false); const [title, setTitle] = useState(PROJECT_NAME); const { pathname } = useLocation(); - const handleDrawerToggle = () => { - setMobileOpen(!mobileOpen); - }; + // Memoize drawer toggle handler to prevent unnecessary re-renders + const handleDrawerToggle = useCallback(() => { + setMobileOpen((prev) => !prev); + }, []); - useEffect(() => setMobileOpen(false), [pathname]); + // Close drawer when route changes + useEffect(() => { + setMobileOpen(false); + }, [pathname]); - // cache the object to prevent unnecessary re-renders - const obj = useMemo(() => ({ title, setTitle }), [title]); + // Memoize context value to prevent unnecessary re-renders + const contextValue = useMemo(() => ({ title, setTitle }), [title]); return ( - + @@ -37,6 +41,6 @@ const Layout: FC = ({ children }) => { ); -}; +}); export default Layout; diff --git a/interface/src/components/loading/LazyLoader.tsx b/interface/src/components/loading/LazyLoader.tsx index d21e0a147..80d6f2d84 100644 --- a/interface/src/components/loading/LazyLoader.tsx +++ b/interface/src/components/loading/LazyLoader.tsx @@ -1,6 +1,8 @@ +import { memo } from 'react'; + import { Box, CircularProgress } from '@mui/material'; -const LazyLoader = () => ( +const LazyLoader = memo(() => ( ( > -); +)); export default LazyLoader; diff --git a/interface/src/utils/useRest.ts b/interface/src/utils/useRest.ts index fcc9fc54c..6800b0c4d 100644 --- a/interface/src/utils/useRest.ts +++ b/interface/src/utils/useRest.ts @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { useBlocker } from 'react-router'; import { toast } from 'react-toastify'; @@ -35,20 +35,26 @@ export const useRest = ({ read, update }: RestRequestOptions) => { setDirtyFlags([]); }); - const updateDataValue = (new_data: D) => { - updateData({ data: new_data }); - }; + // Memoize updateDataValue to prevent unnecessary re-renders + const updateDataValue = useCallback( + (new_data: D) => { + updateData({ data: new_data }); + }, + [updateData] + ); - const loadData = async () => { + // Memoize loadData to prevent unnecessary re-renders + const loadData = useCallback(async () => { setDirtyFlags([]); setErrorMessage(undefined); await readData().catch((error: Error) => { toast.error(error.message); setErrorMessage(error.message); }); - }; + }, [readData]); - const saveData = async () => { + // Memoize saveData to prevent unnecessary re-renders + const saveData = useCallback(async () => { if (!data) { return; } @@ -64,14 +70,15 @@ export const useRest = ({ read, update }: RestRequestOptions) => { setErrorMessage(error.message); } }); - }; + }, [data, writeData]); + return { loadData, saveData, saving: saving as boolean, updateDataValue, - data: data as D, // Explicitly define the type of 'data' - origData: origData as D, // Explicitly define the type of 'origData' to 'D' + data: data as D, + origData: origData as D, dirtyFlags, setDirtyFlags, setOrigData,