Next.js中BlockNote onChange异常:输入后组件频繁重渲染
问题原因与解决方案
问题分析
循环重渲染触发机制:
输入内容触发uploadToDatabase后,会调用File A中的updatemutation更新后端数据,导致File A里的document.content同步更新。此时传递给Editor组件的initialContent发生变化,useCreateBlockNote会检测到这个变化并重新初始化整个编辑器实例,直接导致编辑器组件完全重渲染,打断正常输入操作。依赖数组引发的不必要函数重建:
uploadToDatabase的依赖数组包含editor,虽然editor实例本身可能未改变,但React的依赖比对逻辑会认为需要重新创建uploadToDatabase函数。BlockNoteView的onChange属性频繁变化,也会触发组件的无意义重复渲染。
解决方案
固定
initialContent的初始化时机:initialContent仅用于编辑器的首次渲染初始化,后续内容更新由编辑器内部维护,不要随着后端返回的document.content动态变化。可以在File A中用状态仅在组件挂载时赋值初始内容:// File A const [initContent, setInitContent] = useState(document.content); useEffect(() => { setInitContent(document.content); }, []); // 仅在组件挂载时初始化一次 <Editor onChange={onChange} initialContent={initContent} />优化
uploadToDatabase的依赖与编辑器实例获取:
用useRef保存editor实例,避免因editor依赖导致函数频繁重建,同时确保能获取到最新的编辑器文档:// File B const editor: BlockNoteEditor | null = useCreateBlockNote({ initialContent: initialContent ? (JSON.parse(initialContent) as PartialBlock[]) : undefined, }); const editorRef = useRef<BlockNoteEditor | null>(null); useEffect(() => { editorRef.current = editor; }, [editor]); const uploadToDatabase = useCallback(() => { if (onChange && editorRef.current) { setTimeout(() => { onChange(JSON.stringify(editorRef.current.document)); }, 1000); } }, [onChange]); // 仅依赖onChange,避免editor变化导致函数重建增强防抖逻辑(可选):
用更可靠的防抖函数替代单次setTimeout,避免短时间内多次触发更新:// File B const uploadToDatabase = useCallback( _.debounce(() => { if (onChange && editorRef.current) { onChange(JSON.stringify(editorRef.current.document)); } }, 1000), [onChange] ); // 组件卸载时取消未执行的防抖任务 useEffect(() => { return () => uploadToDatabase.cancel(); }, [uploadToDatabase]);
内容的提问来源于stack exchange,提问作者Anabel
相关产品推荐
相关产品推荐

