use memo and callbacks for reduced rendering vis caching

This commit is contained in:
proddy
2025-10-25 15:21:59 +02:00
parent 7ece395d1b
commit 58ae058465
4 changed files with 41 additions and 25 deletions

View File

@@ -1,3 +1,4 @@
import { memo } from 'react';
import type { FC } from 'react'; import type { FC } from 'react';
import { CssBaseline, ThemeProvider, responsiveFontSizes } from '@mui/material'; import { CssBaseline, ThemeProvider, responsiveFontSizes } from '@mui/material';
@@ -5,6 +6,7 @@ import { createTheme } from '@mui/material/styles';
import type { RequiredChildrenProps } from 'utils'; import type { RequiredChildrenProps } from 'utils';
// Memoize dialog style to prevent recreation
export const dialogStyle = { export const dialogStyle = {
'& .MuiDialog-paper': { '& .MuiDialog-paper': {
borderRadius: '8px', borderRadius: '8px',
@@ -12,8 +14,9 @@ export const dialogStyle = {
borderStyle: 'solid', borderStyle: 'solid',
borderWidth: '1px' borderWidth: '1px'
} }
}; } as const;
// Memoize theme creation to prevent recreation
const theme = responsiveFontSizes( const theme = responsiveFontSizes(
createTheme({ createTheme({
typography: { typography: {
@@ -34,11 +37,11 @@ const theme = responsiveFontSizes(
}) })
); );
const CustomTheme: FC<RequiredChildrenProps> = ({ children }) => ( const CustomTheme: FC<RequiredChildrenProps> = memo(({ children }) => (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<CssBaseline /> <CssBaseline />
{children} {children}
</ThemeProvider> </ThemeProvider>
); ));
export default CustomTheme; export default CustomTheme;

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import type { FC } from 'react'; import type { FC } from 'react';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
@@ -13,22 +13,26 @@ import { LayoutContext } from './context';
export const DRAWER_WIDTH = 210; export const DRAWER_WIDTH = 210;
const Layout: FC<RequiredChildrenProps> = ({ children }) => { const Layout: FC<RequiredChildrenProps> = memo(({ children }) => {
const [mobileOpen, setMobileOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false);
const [title, setTitle] = useState(PROJECT_NAME); const [title, setTitle] = useState(PROJECT_NAME);
const { pathname } = useLocation(); const { pathname } = useLocation();
const handleDrawerToggle = () => { // Memoize drawer toggle handler to prevent unnecessary re-renders
setMobileOpen(!mobileOpen); 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 // Memoize context value to prevent unnecessary re-renders
const obj = useMemo(() => ({ title, setTitle }), [title]); const contextValue = useMemo(() => ({ title, setTitle }), [title]);
return ( return (
<LayoutContext.Provider value={obj}> <LayoutContext.Provider value={contextValue}>
<LayoutAppBar title={title} onToggleDrawer={handleDrawerToggle} /> <LayoutAppBar title={title} onToggleDrawer={handleDrawerToggle} />
<LayoutDrawer mobileOpen={mobileOpen} onClose={handleDrawerToggle} /> <LayoutDrawer mobileOpen={mobileOpen} onClose={handleDrawerToggle} />
<Box component="main" sx={{ marginLeft: { md: `${DRAWER_WIDTH}px` } }}> <Box component="main" sx={{ marginLeft: { md: `${DRAWER_WIDTH}px` } }}>
@@ -37,6 +41,6 @@ const Layout: FC<RequiredChildrenProps> = ({ children }) => {
</Box> </Box>
</LayoutContext.Provider> </LayoutContext.Provider>
); );
}; });
export default Layout; export default Layout;

View File

@@ -1,6 +1,8 @@
import { memo } from 'react';
import { Box, CircularProgress } from '@mui/material'; import { Box, CircularProgress } from '@mui/material';
const LazyLoader = () => ( const LazyLoader = memo(() => (
<Box <Box
display="flex" display="flex"
justifyContent="center" justifyContent="center"
@@ -15,6 +17,6 @@ const LazyLoader = () => (
> >
<CircularProgress size={40} /> <CircularProgress size={40} />
</Box> </Box>
); ));
export default LazyLoader; export default LazyLoader;

View File

@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useCallback, useState } from 'react';
import { useBlocker } from 'react-router'; import { useBlocker } from 'react-router';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
@@ -35,20 +35,26 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
setDirtyFlags([]); setDirtyFlags([]);
}); });
const updateDataValue = (new_data: D) => { // Memoize updateDataValue to prevent unnecessary re-renders
const updateDataValue = useCallback(
(new_data: D) => {
updateData({ data: new_data }); updateData({ data: new_data });
}; },
[updateData]
);
const loadData = async () => { // Memoize loadData to prevent unnecessary re-renders
const loadData = useCallback(async () => {
setDirtyFlags([]); setDirtyFlags([]);
setErrorMessage(undefined); setErrorMessage(undefined);
await readData().catch((error: Error) => { await readData().catch((error: Error) => {
toast.error(error.message); toast.error(error.message);
setErrorMessage(error.message); setErrorMessage(error.message);
}); });
}; }, [readData]);
const saveData = async () => { // Memoize saveData to prevent unnecessary re-renders
const saveData = useCallback(async () => {
if (!data) { if (!data) {
return; return;
} }
@@ -64,14 +70,15 @@ export const useRest = <D>({ read, update }: RestRequestOptions<D>) => {
setErrorMessage(error.message); setErrorMessage(error.message);
} }
}); });
}; }, [data, writeData]);
return { return {
loadData, loadData,
saveData, saveData,
saving: saving as boolean, saving: saving as boolean,
updateDataValue, updateDataValue,
data: data as D, // Explicitly define the type of 'data' data: data as D,
origData: origData as D, // Explicitly define the type of 'origData' to 'D' origData: origData as D,
dirtyFlags, dirtyFlags,
setDirtyFlags, setDirtyFlags,
setOrigData, setOrigData,