React布局状态变更致Outlet子路由重复渲染问题求助
解决React Layout切换侧边栏时Outlet页面重复渲染的问题
问题根源
你的MainLayout组件里,Main和DrawerHeader这两个styled组件是定义在组件内部的。每次侧边栏的open状态变化,MainLayout重渲染时,这两个styled组件都会被重新创建——React会把它们识别成全新的组件类型,进而强制整个子组件树(包括Outlet里的页面)跟着重渲染,这就是为什么页面里的useEffect会重复触发API请求。
用memo包裹Outlet没用,因为问题不在Outlet本身,而是父级的styled组件被反复重建导致的连锁反应。
修复方案
把styled组件移到MainLayout组件的外部,这样它们只会在组件初始化时创建一次,不会随状态变化重新定义。修改后的Layout代码如下:
//react import { Outlet } from 'react-router-dom'; import { useState } from 'react'; //mui import { styled } from '@mui/material/styles'; //project import { Header } from '../features/Header'; import { SideBar } from '../features/SideBar'; import { BackgroundBox } from '../core/BackgroundBox'; const drawerWidth = 240; // 将styled组件移到组件外部,避免每次重渲染重建 const Main = styled('main', { shouldForwardProp: (prop) => prop !== 'open' })( ({ theme, open }) => ({ flexGrow: 1, padding: theme.spacing(3), transition: theme.transitions.create('margin', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen }), marginLeft: 0, ...(open && { transition: theme.transitions.create('margin', { easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen }), marginLeft: `${drawerWidth}px` }) }) ); const DrawerHeader = styled('div')(({ theme}) => ({ display: 'flex', alignItems: 'center', padding: theme.spacing(0, 1), ...theme.mixins.toolbar, justifyContent: 'flex-end' })); const MainLayout = () => { const [open, setOpen] = useState(false); return ( <BackgroundBox> <Header open={open} setOpen={setOpen} DrawerHeader={DrawerHeader} drawerWidth={drawerWidth} /> <SideBar open={open} drawerWidth={drawerWidth} setOpen={setOpen} /> <Main open={open}> <Outlet /> </Main> </BackgroundBox> ); }; export default MainLayout;
额外优化提示
- 检查Header和SideBar组件,如果里面也有类似在内部定义styled组件或其他组件的情况,同样移到组件外部,进一步避免不必要的重渲染。
- 页面组件(比如SamplePage1)如果后续遇到props变化导致的重渲染,可以用
React.memo包裹,但当前问题的核心在Layout,先解决这个就能阻止API重复请求了。
内容的提问来源于stack exchange,提问作者Gavin Cookman
相关产品推荐
相关产品推荐

