You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中CKEditor 5随状态/属性变更重复渲染问题求助

解决方案:Next.js + CKEditor5 输入时重渲染失焦问题

核心问题分析

问题根源有两点:

  1. 动态导入的Editor组件定义在Create组件内部,每次父组件因setText触发重渲染时,都会重新执行dynamic导入,导致编辑器组件被重新初始化,进而失去焦点。
  2. 父组件将text状态作为data props传递给子组件,每次输入触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 22:55:24