如何使用useEffect为Tiptap预览模态框添加/移除事件监听器
解决Tiptap预览模态框事件监听问题
核心问题分析
ref绑定在了DialogContent上,但实际需要监听的是内部动态生成的预览内容容器,导致无法获取目标元素- 仅以
open作为useEffect依赖,无法保证模态框DOM完全渲染后再执行事件绑定,此时ref.current可能为空 - 预览内容随编辑器动态变化,需要在内容更新时重新绑定监听器
修复方案
1. 调整ref绑定目标
把ref直接绑定到预览内容的div上,确保能精准获取到要监听的元素。
2. 优化useEffect的依赖与执行时机
- 增加
editor.getHTML()作为依赖,确保内容变化时重新绑定监听器 - 使用
useLayoutEffect(或在useEffect中通过requestAnimationFrame确保DOM渲染完成),避免DOM未挂载时绑定失败 - 完善监听器清理逻辑,每次绑定前先移除旧监听器,防止重复绑定
3. 修正onOpenChange函数写法
当前嵌套函数写法会导致逻辑执行异常,改为直接接收open参数的函数。
完整修复代码
type Props = { editor: Editor; }; export const PreviewButton = ({ editor }: Props) => { const pathname = usePathname(); const [open, setOpen] = useState(false); const previewRef = useRef<HTMLDivElement>(null); // 绑定预览内容容器的ref const handleClick = () => { console.log(pathname); setOpen(true); return true; }; useLayoutEffect(() => { if (!open || !previewRef.current) return; const handlePreviewClick = () => console.log('Hi'); const previewContent = previewRef.current; // 绑定点击监听器 previewContent.addEventListener('click', handlePreviewClick); // 清理函数:移除监听器,避免内存泄漏 return () => { previewContent.removeEventListener('click', handlePreviewClick); }; }, [open, editor.getHTML()]); // 依赖open和编辑器内容,确保内容变化时重新绑定 const onOpenChange = (isOpen: boolean) => { editor.setEditable(!isOpen); setOpen(isOpen); }; const modalContent = ( <DialogContent> <DialogHeader> <DialogTitle>Podgląd</DialogTitle> </DialogHeader> {open && ( <div ref={previewRef} style={{ maxWidth: '800px' }} className='ProseMirror prose dark:prose-invert' dangerouslySetInnerHTML={{ __html: editor.getHTML() }} /> )} </DialogContent> ); return ( <MenuButton icon={<PreviewIcon />} command={handleClick} tooltip={{ content: <TooltipContent title={_t('commands.preview')} />, }} modal={{ isModalButton: true, modalContent: modalContent, isOpen: open, setIsOpen: setOpen, onOpenChange: onOpenChange, }} /> ); };
额外说明
useLayoutEffect会在DOM更新完成后立即执行,确保previewRef.current能获取到已挂载的元素- 依赖
editor.getHTML()保证每次编辑器内容变化、模态框重新打开时,监听器都会绑定到最新的内容上 - 移除了
lastContent状态,模态框打开时直接渲染最新的编辑器内容即可,无需缓存旧内容
内容的提问来源于stack exchange,提问作者Merger
相关产品推荐
相关产品推荐

