I have created a lodable component,
const componentsMap = new Map();
const useCachedLazy = (importFunc, namedExport) => {
if (!componentsMap.has(importFunc)) {
const LazyComponent = lazy(() =>
importFunc().then(module => ({
default: namedExport ? module[namedExport] : module.default,
}))
);
componentsMap.set(importFunc, LazyComponent);
}
return componentsMap.get(importFunc);
};
const loadable = (importFunc, { namedExport = null } = {}) => {
const MemoizedLazyComponent = React.memo(props => {
const LazyComponent = useCachedLazy(importFunc, namedExport);
return (
<LazyComponent {...props} />
);
});
return MemoizedLazyComponent;
};
export default loadable;
You can see that I have tried caching the lazy component here using memo and useCachedLazy.
My routes are called in App.js like this:
<Suspense fallback={<div><Spin /></div>}>
<Switch>
{routes.map(route => {
const LazyComponent = loadable(
() => Promise.resolve({ default: route.component }),
{ namedExport: route.namedExport } // passing named exports to loadable
);
return route.auth ? (
<PrivateRoute
{...route}
key={route.key}
collapsed={collapsed}
toggleCollapsed={toggleCollapsed}
showHiddenTriggerKeyPress={showHiddenTriggerKeyPress}
component={LazyComponent}
locale={locale}
/>
) : (
<Route {...route} key={route.key} component={LazyComponent} />
);
})}
</Switch>
</Suspense>
in routes array, I pass the component directly in the prop route.component.
Now lazy loading has really improved loading speed for me, so this is a good thing. But whenever something changes, all components are remounting instead they should be re-rendered. Please mention any probable fixes for this.
EDIT I didn't mention an example previously on how these components are remounting. So here's an example, I have setup a key, shift+X in my app.js which I use to show/hide some hidden items in my components.
const shiftXKeyPress = useKeyPress('X');
const showHiddenTriggerKeyPress = shiftXKeyPress;
function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = useState(false);
let prevKey = '';
function downHandler({ key }) {
if (prevKey === targetKey) return;
if (key === targetKey) {
setKeyPressed(!keyPressed);
prevKey = targetKey;
}
}
useEffect(() => {
window.addEventListener('keydown', downHandler);
return () => {
window.removeEventListener('keydown', downHandler);
};
}, [keyPressed]);
return keyPressed;
}
I have passed showHiddenTriggerKeyPress as a prop to all components, see in the above code for App.js. So whenever I press shift+x it should just re-render and display related contents, instead the whole component is reloading.
This will be a long answer. So I made some of my custom configurations to fix this, also took help from the answer by @ShehanLakshita.
Firstly I wrapped PrivateRoute and Route with memo to prevent unnecessary re renders,
const MemoizedRoute = React.memo(({ isPrivate, component: Component, ...rest }) => {
if (isPrivate) {
return (
<PrivateRoute
{...rest}
component={Component}
/>
);
}
return (
<Route
{...rest}
render={routeProps => <Component {...routeProps} {...rest} />}
/>
);
});
I modified my loadable component to cache the LazyComponent correctly as suggested by @Shehan,
const cachedComponentsMap = new Map();
const useCachedLazy = (importFunc, namedExport) => {
try {
if (!cachedComponentsMap.has(importFunc)) {
const LazyComponent = lazy(() =>
importFunc().then(module => ({
default: namedExport ? module[namedExport] : module.default,
}))
);
cachedComponentsMap.set(importFunc, LazyComponent);
}
return cachedComponentsMap.get(importFunc);
} catch (error) {
console.error('Error loading component:', error);
throw error;
}
};
const loadable = (importFunc, { namedExport = null } = {}) => {
const cachedKey = `${importFunc}-${namedExport}`;
if (!cachedComponentsMap.has(cachedKey)) {
const MemoizedLazyComponent = React.memo(props => {
const LazyComponent = useCachedLazy(importFunc, namedExport);
return <LazyComponent {...props} />;
});
cachedComponentsMap.set(cachedKey, MemoizedLazyComponent);
}
return cachedComponentsMap.get(cachedKey);
};
Now the components do not unmount and remount unnecessary and I also got lazy working and the performance is improved.