React中如何将Material-UI Drawer宽度设置为其子组件的宽度?
动态获取Drawer子组件宽度实现内容自适应收缩
我懂你现在的需求——想要让Drawer的宽度自动匹配<YourProfile>的实际渲染宽度,替代固定值,这样Drawer展开时左侧内容能完美适配、不会出现重叠。下面是具体的实现方案:
核心思路
我们通过React Refs获取<YourProfile>的实际宽度,再把这个值动态应用到Drawer的宽度设置中,同时调整左侧内容的可用空间,避免内容重叠。另外还要考虑窗口大小变化、组件内容动态更新的场景,确保宽度能实时同步。
修改后的完整代码
import { useState, useRef, useEffect } from "react"; // Material UI components import { Box, Button, Drawer, Divider, Grid, IconButton } from "@mui/material"; // Components import DashboardHeader from "./DashboardHeader/DashboardHeader"; import LeadershipProfile from "./LeadershipProfile/LeadershipProfile"; import YourProfile from "./YourProfile/YourProfile"; import CustomIcon from "components/CustomIcon"; import icons from "enums/icons"; import { Theme } from "@mui/material/styles"; import makeStyles from "@mui/styles/makeStyles"; import createStyles from "@mui/styles/createStyles"; const useStyles = makeStyles((theme: Theme) => createStyles({ leftSideContainer: { display: "contents", overflow: "auto", textAlign: "center", flexGrow: 1, // 让左侧容器占满剩余可用空间 }, drawer: { top: theme.spacing(9), "& .MuiDrawer-paper": { marginTop: "72px", position: "absolute", // 移除固定宽度,后续用sx动态赋值 }, }, drawerButton: { color: theme.palette.button.active.from, height: "14px", }, }) ); const PaceLabsDashboard = () => { const classes = useStyles(); const [isDrawerOpen, setIsDrawerOpen] = useState(true); const [drawerContentWidth, setDrawerContentWidth] = useState(0); const profileRef = useRef<HTMLDivElement>(null); // 创建Ref绑定到YourProfile的外层容器 // 获取YourProfile的实际宽度 const getProfileWidth = () => { if (profileRef.current) { setDrawerContentWidth(profileRef.current.offsetWidth); } }; // 组件挂载后获取初始宽度,同时监听窗口resize更新宽度 useEffect(() => { getProfileWidth(); window.addEventListener("resize", getProfileWidth); return () => { window.removeEventListener("resize", getProfileWidth); }; }, []); // 监听YourProfile自身尺寸变化(应对内容动态更新的场景) useEffect(() => { if (!profileRef.current) return; const observer = new ResizeObserver(getProfileWidth); observer.observe(profileRef.current); return () => { observer.disconnect(); }; }, []); return ( <Box> {/* Header */} <Box sx={{ border: "2px solid black", width: "100%", height: "72px", textAlign: "center" }}> <DashboardHeader /> </Box> <Box sx={{ display: "flex", width: "100%" }}> {/* 右侧内容区域 */} <Box className={classes.leftSideContainer} sx={{ // Drawer展开时,给左侧容器设置marginRight,留出Drawer的宽度 ...(isDrawerOpen ? { marginRight: `${drawerContentWidth}px` } : { marginRight: 0 }) }} > <LeadershipProfile /> <IconButton size="small" aria-label="toggle-drawer" onClick={() => setIsDrawerOpen(!isDrawerOpen)}> <CustomIcon className={classes.drawerButton} icon={isDrawerOpen ? icons.chevronRightThin : icons.chevronLeftThin} /> </IconButton> </Box> {/* 左侧Drawer区域 */} <Drawer anchor="right" variant="persistent" open={isDrawerOpen} className={classes.drawer} sx={{ display: isDrawerOpen ? "flex" : { xs: "block", sm: "none" }, "& .MuiDrawer-paper": { width: `${drawerContentWidth}px`, // 动态设置Drawer宽度 } }} > {/* 将Ref绑定到YourProfile的外层容器 */} <Box ref={profileRef}> <YourProfile /> </Box> </Drawer> </Box> </Box> ); }; export default PaceLabsDashboard;
关键细节说明
- Ref获取实际宽度:通过
profileRef绑定到<YourProfile>的外层容器,直接拿到组件渲染后的真实宽度。 - 多场景宽度同步:
- 窗口resize监听:确保窗口大小变化时,宽度能实时更新。
- ResizeObserver监听:如果
<YourProfile>的内容是动态加载或变化的,这个API能监听元素自身的尺寸变化,比单纯的窗口resize更精准。
- 动态样式适配:给左侧容器设置
marginRight,Drawer展开时自动留出对应宽度,彻底避免内容重叠;同时将Drawer的宽度设置为获取到的实际值,保证Drawer完全贴合子组件尺寸。
这样修改后,Drawer的宽度会完全匹配<YourProfile>的实际宽度,展开时左侧内容也会自动收缩,完美解决重叠问题!
内容的提问来源于stack exchange,提问作者CatroVal
相关产品推荐
相关产品推荐

