如何缓存React懒加载组件并避免不必要的卸载与重新挂载?
问题修复:懒加载组件状态变化时重复挂载而非重渲染
核心问题原因
每次App组件重渲染(比如showHiddenTriggerKeyPress状态变化)时,routes.map内部会重复调用loadable函数,每次调用都会生成全新的MemoizedLazyComponent实例。React路由检测到component属性引用变化后,会卸载旧组件并重新挂载新组件,而非仅触发重渲染。
另外你的useKeyPress钩子存在逻辑缺陷:用let prevKey保存上一次按键会在组件重渲染时被重置,且未判断shiftKey,可能导致快捷键触发异常。
修复方案
1. 全局缓存loadable生成的组件实例
修改loadable函数,新增全局缓存存储已生成的MemoizedLazyComponent,确保相同参数下返回同一个组件实例:
const componentsMap = new Map(); // 新增全局缓存,存储已生成的Memoized组件 const memoizedComponentsMap = 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 } = {}) => { // 用importFunc和namedExport组合生成唯一缓存键 const cacheKey = `${importFunc}-${namedExport}`; if (memoizedComponentsMap.has(cacheKey)) { return memoizedComponentsMap.get(cacheKey); } const MemoizedLazyComponent = React.memo(props => { const LazyComponent = useCachedLazy(importFunc, namedExport); return <LazyComponent {...props} />; }); memoizedComponentsMap.set(cacheKey, MemoizedLazyComponent); return MemoizedLazyComponent; }; export default loadable;
2. 在App组件中缓存路由懒加载组件
避免在routes.map中每次渲染都调用loadable,用useMemo缓存生成的懒加载组件列表:
// App.js内部 const lazyRouteComponents = useMemo(() => { return routes.map(route => ({ ...route, lazyComponent: loadable( () => Promise.resolve({ default: route.component }), { namedExport: route.namedExport } ) })); }, [routes]); // 如果routes是静态常量,此依赖可确保仅初始化一次 // 渲染路由时使用缓存后的lazyComponent <Suspense fallback={<div><Spin /></div>}> <Switch> {lazyRouteComponents.map(route => { return route.auth ? ( <PrivateRoute {...route} key={route.key} collapsed={collapsed} toggleCollapsed={toggleCollapsed} showHiddenTriggerKeyPress={showHiddenTriggerKeyPress} component={route.lazyComponent} locale={locale} /> ) : ( <Route {...route} key={route.key} component={route.lazyComponent} /> ); })} </Switch> </Suspense>
3. 修复useKeyPress钩子逻辑
用useRef持久化上一次按键状态,并增加shiftKey判断,确保仅在Shift+X时触发状态变化:
function useKeyPress(targetKey) { const [keyPressed, setKeyPressed] = useState(false); const prevKeyRef = useRef(''); // 用useRef持久化状态 function downHandler({ key, shiftKey }) { // 仅在Shift+X且未重复触发时更新状态 if (key === targetKey && shiftKey && prevKeyRef.current !== targetKey) { setKeyPressed(prev => !prev); prevKeyRef.current = targetKey; } // 按键抬起时重置prevKey const upHandler = () => { prevKeyRef.current = ''; }; window.addEventListener('keyup', upHandler); return () => window.removeEventListener('keyup', upHandler); } useEffect(() => { window.addEventListener('keydown', downHandler); return () => { window.removeEventListener('keydown', downHandler); }; }, [targetKey]); // 依赖仅为targetKey,避免重复绑定事件 return keyPressed; } // 使用时 const showHiddenTriggerKeyPress = useKeyPress('X');
验证效果
修改完成后,按下Shift+X触发showHiddenTriggerKeyPress状态变化时,路由组件会仅执行重渲染,而非卸载后重新挂载,同时快捷键逻辑也会更稳定。
内容的提问来源于stack exchange,提问作者PoorProgrammer
相关产品推荐
相关产品推荐

