Next.js中CKEditor 5随状态/属性变更重复渲染问题求助
解决方案:Next.js + CKEditor5 输入时重渲染失焦问题
核心问题分析
问题根源有两点:
- 动态导入的
Editor组件定义在Create组件内部,每次父组件因setText触发重渲染时,都会重新执行dynamic导入,导致编辑器组件被重新初始化,进而失去焦点。 - 父组件将
text状态作为dataprops传递给子组件,每次输入触发setText后,data更新会强制同步CKEditor内容,加剧重渲染问题。
最优方案:避免不必要的重渲染
步骤1:将动态导出移到组件外部
把dynamic导入语句移到Create组件顶部,确保编辑器组件只被加载一次:
// Create.js 页面代码修改 import dynamic from "next/dynamic"; import { useState } from 'react'; // 移到组件外部,避免每次重渲染重新导入 const Editor = dynamic(() => import("../components/MyEditor"), { ssr: false }); export default function Create() { const [text, setText] = useState("") const handleTextInput = (textInput) => { setText(textInput) } return ( <> <div> <Editor handleInput={handleTextInput} data={text} /> </div> </> ) }
步骤2:优化子组件的props同步逻辑
如果不需要实时将编辑器内容同步到父组件状态(比如仅在提交时获取内容),可以移除data props传递,让编辑器维护自身内部状态:
// MyEditor.js 组件代码修改 import Editor from '../../ckeditor5-custom-build/build/ckeditor' import { CKEditor } from '@ckeditor/ckeditor5-react' import '../../ckeditor5-custom-build/build/translations/de'; const MyEditor = (props) => { const editorConfiguration = { toolbar: { items: ['bold', 'italic', 'underline', '|', 'undo', 'redo'], } }; return ( <CKEditor editor={Editor} config={editorConfiguration} onChange={(event, editor) => { props.handleInput?.(editor.getData()) }} /> ); } export default MyEditor
如果必须实时同步父组件状态到编辑器(比如从父组件初始化内容),用useEffect监听data变化,仅在外部主动更新时同步:
// MyEditor.js 优化版本 import { useEffect, useRef } from 'react'; import Editor from '../../ckeditor5-custom-build/build/ckeditor' import { CKEditor } from '@ckeditor/ckeditor5-react' import '../../ckeditor5-custom-build/build/translations/de'; const MyEditor = (props) => { const editorConfiguration = { toolbar: { items: ['bold', 'italic', 'underline', '|', 'undo', 'redo'], } }; const editorRef = useRef(null); // 仅当外部data变化时,同步到编辑器 useEffect(() => { if (editorRef.current && props.data !== editorRef.current.getData()) { editorRef.current.setData(props.data); } }, [props.data]); return ( <CKEditor editor={Editor} config={editorConfiguration} onReady={(editor) => { editorRef.current = editor; }} onChange={(event, editor) => { props.handleInput(editor.getData()) }} /> ); } export default MyEditor
备选方案1:仅在提交时更新父组件状态
如果不需要实时同步,只在提交时获取编辑器内容,通过ref传递编辑器实例:
父组件修改:
import dynamic from "next/dynamic"; import { useState, useRef } from 'react'; const Editor = dynamic(() => import("../components/MyEditor"), { ssr: false }); export default function Create() { const editorRef = useRef(null); const [text, setText] = useState("") const handleSubmit = () => { if (editorRef.current) { const content = editorRef.current.getData(); setText(content); // 执行提交逻辑 } } return ( <> <div> <Editor editorRef={editorRef} /> </div> <button onClick={handleSubmit}>提交</button> </> ) }
子组件修改:
import { useImperativeHandle, forwardRef, useRef } from 'react'; import Editor from '../../ckeditor5-custom-build/build/ckeditor' import { CKEditor } from '@ckeditor/ckeditor5-react' import '../../ckeditor5-custom-build/build/translations/de'; // 使用forwardRef传递编辑器实例 const MyEditor = forwardRef((props, ref) => { const editorConfiguration = { toolbar: { items: ['bold', 'italic', 'underline', '|', 'undo', 'redo'], } }; const internalEditorRef = useRef(null); // 暴露getData方法给父组件 useImperativeHandle(ref, () => ({ getData: () => internalEditorRef.current?.getData() || "" })); return ( <CKEditor editor={Editor} config={editorConfiguration} onReady={(editor) => { internalEditorRef.current = editor; }} /> ); }); export default MyEditor;
备选方案2:手动恢复焦点(兜底方案)
如果以上方案无效,可在编辑器重渲染后手动恢复焦点:
// MyEditor.js 添加焦点恢复逻辑 import { useEffect, useRef } from 'react'; import Editor from '../../ckeditor5-custom-build/build/ckeditor' import { CKEditor } from '@ckeditor/ckeditor5-react' import '../../ckeditor5-custom-build/build/translations/de'; const MyEditor = (props) => { const editorConfiguration = { toolbar: { items: ['bold', 'italic', 'underline', '|', 'undo', 'redo'], } }; const editorRef = useRef(null); const shouldFocus = useRef(true); useEffect(() => { if (editorRef.current && shouldFocus.current) { editorRef.current.focus(); shouldFocus.current = false; } }, [props.data]); return ( <CKEditor editor={Editor} config={editorConfiguration} data={props.data} onReady={(editor) => { editorRef.current = editor; }} onChange={(event, editor) => { props.handleInput(editor.getData()) shouldFocus.current = true; }} /> ); } export default MyEditor
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

