如何正确实现React Quill自定义工具栏的条件渲染?
React Quill自定义工具栏隐藏后无法重新渲染的解决办法
问题说明
在使用React Quill自定义工具栏时,通过isOpen状态控制工具栏的显示与隐藏。但切换isOpen为false后再设为true,带有id="toolbar"的工具栏按钮区域无法正常渲染,仅右侧的操作按钮能显示。
原因分析
- 组件重复定义:
CustomToolbar被定义在OpinionInput组件内部,每次父组件渲染都会重新创建这个组件,导致DOM元素被重新生成。而ReactQuill实例初始化时只会绑定一次#toolbar容器,新生成的容器无法被已存在的Quill实例识别。 - 显示控制方式不当:使用
display: none仅隐藏DOM而非卸载组件,再次显示时Quill不会重新初始化工具栏的绑定关系,导致按钮无法正常工作。
解决方案
方案一:调整组件结构+条件渲染+强制重新初始化
将工具栏组件移到外部,用条件渲染控制挂载/卸载,并给ReactQuill加key强制重新渲染,让Quill重新绑定工具栏:
import { useState } from 'react'; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; import './OpinionInput.css'; const OPINION_PLACEHOLDER = 'Qual sua opinião?'; // 移到组件外部定义,避免重复创建 const CustomToolbar = ({ onClose }) => { return ( <div className="flex justify-between items-center"> <div id="toolbar"> <button className="ql-bold"></button> <button className="ql-italic"></button> </div> <div className="space-x-3"> <button className="ql-custom text-grey-500 text-center focus:outline-none hover:text-grey-700 hover:bg-grey-100" onClick={onClose} > Cancelar </button> <button className="bg-brandGreen text-greys-0 px-3 py-1 rounded-full"> Opinar </button> </div> </div> ); }; export default function OpinionInput() { const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const handleOpen = () => { setIsOpen(true); }; const handleClose = () => { setIsOpen(false); }; return ( <div className="flex flex-col"> {/* 条件渲染用户信息栏 */} {isOpen && ( <div className="flex flex-row items-center space-x-2"> <div className="w-8 h-8 bg-brandGreen rounded-full"></div> <span className="text-body-medium">Pedro Silva</span> </div> )} <ReactQuill key={isOpen} // 用isOpen作为key,状态变化时强制重新渲染组件 theme="snow" value={value} onChange={setValue} placeholder={OPINION_PLACEHOLDER} modules={{ toolbar: { container: '#toolbar' }, }} onFocus={handleOpen} /> {/* 条件渲染工具栏,替代display:none */} {isOpen && <CustomToolbar onClose={handleClose} />} </div> ) }
方案二:使用ReactQuill内置工具栏配置(更推荐)
直接通过ReactQuill的toolbar配置数组自定义工具栏,避免手动操作DOM容器,更符合React组件化思想:
import { useState } from 'react'; import ReactQuill from 'react-quill'; import 'react-quill/dist/quill.snow.css'; import './OpinionInput.css'; const OPINION_PLACEHOLDER = 'Qual sua opinião?'; export default function OpinionInput() { const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(''); const handleOpen = () => { setIsOpen(true); }; const handleClose = () => { setIsOpen(false); }; // 自定义工具栏配置,整合默认按钮和自定义操作按钮 const toolbarOptions = [ ['bold', 'italic'], // 指定自定义按钮的容器 [{ container: '#custom-toolbar-actions' }] ]; return ( <div className="flex flex-col"> {isOpen && ( <div className="flex flex-row items-center space-x-2"> <div className="w-8 h-8 bg-brandGreen rounded-full"></div> <span className="text-body-medium">Pedro Silva</span> </div> )} <ReactQuill theme="snow" value={value} onChange={setValue} placeholder={OPINION_PLACEHOLDER} modules={{ toolbar: toolbarOptions }} onFocus={handleOpen} /> {isOpen && ( <div id="custom-toolbar-actions" className="flex justify-end space-x-3 mt-2"> <button className="ql-custom text-grey-500 text-center focus:outline-none hover:text-grey-700 hover:bg-grey-100" onClick={handleClose} > Cancelar </button> <button className="bg-brandGreen text-greys-0 px-3 py-1 rounded-full"> Opinar </button> </div> )} </div> ) }
方案二无需手动处理DOM绑定问题,完全依托ReactQuill的原生能力实现自定义,能有效避免类似的渲染bug。
内容的提问来源于stack exchange,提问作者PedroB
相关产品推荐
相关产品推荐

