如何阻止Material UI ModalUnstyled在showSpinner时点击遮罩层关闭
解决Material UI ModalUnstyled在showSpinner时点击遮罩层关闭的问题
问题场景
使用Material UI的ModalUnstyled组件时,当showSpinner为true,点击遮罩层(backdrop)仍会触发模态框关闭,尽管已经尝试通过条件化onClickClose来阻止:
onClose={!showSpinner ? onClickClose : () => {}}
核心原因
ModalUnstyled会给遮罩层元素默认绑定点击事件,触发传入的onClose函数。即使自定义了Backdrop组件,这个默认的点击事件依然存在;另外,直接传递空函数给onClose时,若外部show状态的受控逻辑处理不当,仍可能出现意外关闭的情况。
解决方案
方案1:在onClose中过滤backdropClick事件
修改onClose的逻辑,仅当点击原因不是backdropClick,或者showSpinner为false时,才执行关闭操作:
return <ModalUnstyled aria-labelledby={ariaForId} aria-describedby={ariaForId} open={show} onClose={(event, reason) => { // 遮罩层点击且showSpinner为true时,不执行关闭 if (reason === 'backdropClick' && showSpinner) return; onClickClose(); }} className={'modal-container'} slots={{backdrop: BackdropUnstyled}}> {/* 模态框内容 */} </ModalUnstyled>
方案2:阻止自定义Backdrop内的事件冒泡
在自定义的Backdrop组件中,给最外层div添加点击事件并阻止冒泡,避免触发ModalUnstyled默认的遮罩层点击事件:
const BackdropUnstyled = React.forwardRef<HTMLDivElement, { open?: boolean; className: string }>((props, ref) => { const {ownerState, open, className, ...other} = props; const handleBackdropClick = (e: React.MouseEvent) => { if (showSpinner) { e.stopPropagation(); return; } onClickClose(); }; return <div className={clsx({'MuiBackdrop-open': open}, 'modal-backdrop', className)} ref={ref} onClick={handleBackdropClick} {...other}> <BlurOverlay onClick={showSpinner ? undefined : onClickClose}/> </div> });
额外优化:同步BlurOverlay的点击逻辑
调整BlurOverlay的onClick传递,确保只有在!showSpinner时才绑定关闭函数,避免不必要的触发:
<BlurOverlay onClick={showSpinner ? undefined : onClickClose}/>
完整修改后的代码示例
export const Modal = ({ children, show, onClickClose, showSpinner, }: ModalProps) => { const ariaForId = `:modal-id-aria-${guidGenerator()}:` const BackdropUnstyled = React.forwardRef< HTMLDivElement, { open?: boolean; className: string } >((props, ref) => { // @ts-ignore const {ownerState, open, className, ...other} = props; const handleBackdropClick = (e: React.MouseEvent) => { if (showSpinner) { e.stopPropagation(); return; } onClickClose(); }; return <div className={clsx({'MuiBackdrop-open': open}, 'modal-backdrop', className)} ref={ref} onClick={handleBackdropClick} {...other}> <BlurOverlay onClick={showSpinner ? undefined : onClickClose}/> </div> }); return <ModalUnstyled aria-labelledby={ariaForId} aria-describedby={ariaForId} open={show} onClose={(event, reason) => { if (reason === 'backdropClick' && showSpinner) return; onClickClose(); }} className={'modal-container'} slots={{backdrop: BackdropUnstyled}}> <div role={'dialog'}> {showSpinner && <div/>} <div> {showSpinner && <Spinner/>} {children} </div> </div> </ModalUnstyled> } interface BlurOverlayProps extends PropsWithOptionalChildren { classes?: string | string[], onClick?: () => void, } export const BlurOverlay = ({ children, classes, onClick, }: BlurOverlayProps) => { const [domElementForBlur, setDomElementForBlur] = useState<HTMLElement | null>(null) useEffect(() => { const elementToApplyBlurTo = document.getElementById('modal-container-anchor') if (elementToApplyBlurTo) { elementToApplyBlurTo.setAttribute('style', 'backdrop-filter: url(#svgBlur); backdrop-filter: blur(5px); width: 100%; height: 100%; position: absolute; top: 0; left: 0') setDomElementForBlur(elementToApplyBlurTo) } return () => { if (elementToApplyBlurTo) elementToApplyBlurTo.setAttribute('style', '') } }, []) const _onClick = () => { if (domElementForBlur) domElementForBlur.setAttribute('style', '') if (onClick) onClick() } return <div className={clsx('blur-overlay-container', classes)} onClick={_onClick}> {children} </div> }
内容的提问来源于stack exchange,提问作者four-eyes
相关产品推荐
相关产品推荐

