基于React的可拖拽可调整大小的Material UI Dialog组件问题排查
解决Material UI Dialog结合react-draggable/react-resizable的两个问题
问题1:全屏后ResizableBox尺寸不更新
解决方案
核心思路是监听Dialog的fullscreen状态变化,通过Ref获取Paper组件的实际尺寸,同步更新ResizableBox的宽高。之前用Ref获取尺寸失败,大概率是因为没有在状态变化后读取最新的DOM尺寸。
代码示例
import { useState, useEffect, useRef } from 'react'; import Dialog from '@mui/material/Dialog'; import Draggable from 'react-draggable'; import { ResizableBox } from 'react-resizable'; import 'react-resizable/css/styles.css'; const CustomDraggableResizableDialog = ({ open, fullscreen, defaultWidth = 500, defaultHeight = 400, ...props }) => { const paperRef = useRef(null); const [resizableSize, setResizableSize] = useState({ width: defaultWidth, height: defaultHeight }); // 监听全屏状态变化,同步尺寸 useEffect(() => { if (!paperRef.current) return; if (fullscreen) { const { offsetWidth, offsetHeight } = paperRef.current; setResizableSize({ width: offsetWidth, height: offsetHeight }); } else { setResizableSize({ width: defaultWidth, height: defaultHeight }); } }, [fullscreen, defaultWidth, defaultHeight]); return ( <Dialog open={open} fullscreen={fullscreen} PaperProps={{ ref: paperRef, sx: { position: 'absolute', // 配合react-draggable的定位逻辑 overflow: 'hidden' // 提前规避滚动条问题 } }} {...props} > <Draggable handle=".dialog-header" bounds="parent"> <div> {/* 拖拽手柄区域 */} <div className="dialog-header" style={{ padding: '12px', cursor: 'move', backgroundColor: '#f5f5f5' }}> 可拖拽对话框 </div> <ResizableBox width={resizableSize.width} height={resizableSize.height} resizeHandles={['e', 's', 'se']} minConstraints={[300, 200]} style={{ boxSizing: 'border-box' }} onResizeStop={(e, data) => setResizableSize({ width: data.size.width, height: data.size.height })} > {/* 对话框内容区域 */} <div style={{ padding: '16px', height: '100%' }}> 这里是对话框内容 </div> </ResizableBox> </div> </Draggable> </Dialog> ); }; export default CustomDraggableResizableDialog;
问题2:设置resizeHandles后出现滚动条
解决方案
滚动条出现的核心原因是resize手柄占用了额外空间,导致内容区域超出Paper的边界。通过以下两步解决:
- 给ResizableBox添加
box-sizing: border-box,让手柄的宽度包含在元素自身尺寸内,不额外占用空间 - 给Dialog的Paper设置
overflow: hidden,直接隐藏超出边界的内容(如果内容确实需要滚动,可以单独给内容区域设置overflow: auto)
关键代码点
- ResizableBox的
style={{ boxSizing: 'border-box' }} - PaperProps的
sx: { overflow: 'hidden' }
如果内容区域需要滚动,把内容容器的样式改成:
<div style={{ padding: '16px', height: '100%', overflow: 'auto' }}> 长内容区域... </div>
内容的提问来源于stack exchange,提问作者Susitha Ravinda Senarath
相关产品推荐
相关产品推荐

