React中如何防止弹窗及确认对话框点击后意外消失?
问题解决:点击确认对话框触发主弹窗外部关闭逻辑的问题
问题核心是:你的确认对话框DOM不在主弹窗的容器内,所以点击它时,会被handleClickOutside判定为“弹窗外部”,进而触发主弹窗的onClose,导致所有弹窗关闭。
下面是几种可行的解决办法:
方法1:将确认对话框嵌套进主弹窗DOM(最推荐)
直接把确认对话框的渲染代码放到主弹窗的popupRef对应的DOM内部,这样主弹窗的contains判断会把确认对话框视为内部元素,点击时就不会触发外部关闭逻辑。
修改主弹窗的渲染代码:
// 主弹窗的容器,popupRef绑定在这里 <div ref={popupRef} className="main-popup"> {/* 主弹窗原有内容 */} <button onClick={() => setShowCancelConfirmationDialog(true)}>取消</button> {/* 把确认对话框移到这里 */} {showCancelConfirmationDialog && <CancelConfirmationDialog onCancel={handleCloseDialog} onConfirm={handleCancelReservation} name={block.lessonWith} time={chosenTime} date={block.day} />} </div>
方法2:给确认对话框添加独立ref,扩展外部点击判断逻辑
如果不能修改DOM结构,就给确认对话框加一个ref,在handleClickOutside里同时判断点击目标是否在确认对话框内,只要在其中一个弹窗内,就不关闭主弹窗。
步骤:
- 给确认对话框创建ref
- 修改
handleClickOutside的判断条件
修改代码:
// 主弹窗ref const popupRef = useRef<HTMLDivElement>(null); // 确认对话框ref const confirmDialogRef = useRef<HTMLDivElement>(null); const handleClickOutside = (event: MouseEvent) => { const isInMainPopup = popupRef.current?.contains(event.target as Node); const isInConfirmDialog = confirmDialogRef.current?.contains(event.target as Node); // 只有当点击既不在主弹窗,也不在确认对话框时,才关闭 if (!isInMainPopup && !isInConfirmDialog) { onClose(); } }; // 渲染确认对话框时绑定ref {showCancelConfirmationDialog && <CancelConfirmationDialog ref={confirmDialogRef} // 这里要修改组件,让它接受ref onCancel={handleCloseDialog} onConfirm={handleCancelReservation} name={block.lessonWith} time={chosenTime} date={block.day} />}
同时需要修改CancelConfirmationDialog组件,让它能接收ref(函数组件默认不支持ref,需要用forwardRef):
import React, { forwardRef } from 'react'; interface DeleteConfirmationDialogProps { onCancel: () => void; onConfirm: () => void; name: string; } // 用forwardRef包裹组件,把ref传递给内部的div const DeleteConfirmationDialog = forwardRef<HTMLDivElement, DeleteConfirmationDialogProps>(({ onCancel, onConfirm, name }, ref) => { return ( <div ref={ref} className="delete-confirmation-dialog"> <p className='delete-confirmation-dialog-p'>Te jazdy są zarezerwowane przez: {name} </p> <p className='delete-confirmation-dialog-p'>Czy na pewno chcesz je przesunąć i powiadomić o tym kursanta?</p> <button className='delete-confirmation-dialog-button' onClick={onConfirm}>Usuń i powiadom</button> <button className='delete-confirmation-dialog-button' onClick={onCancel}>Anuluj</button> </div> ); }); export default DeleteConfirmationDialog;
方法3:用状态临时禁用外部点击关闭逻辑
当确认对话框显示时,暂时移除或禁用handleClickOutside的监听,这样点击确认对话框时不会触发主弹窗关闭。
修改代码:
const [showCancelConfirmationDialog, setShowCancelConfirmationDialog] = useState(false); const popupRef = useRef<HTMLDivElement>(null); const handleClickOutside = (event: MouseEvent) => { if ( popupRef.current && !popupRef.current.contains(event.target as Node) ) { onClose(); } }; useEffect(() => { // 只有当确认对话框不显示时,才添加监听 if (!showCancelConfirmationDialog) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [showCancelConfirmationDialog]); // 依赖加上状态,状态变化时重新执行effect
这样当showCancelConfirmationDialog为true时,不会添加外部点击监听,点击确认对话框就不会触发主弹窗关闭了。
内容的提问来源于stack exchange,提问作者jon
相关产品推荐
相关产品推荐

