Next.js启用reactStrictMode时CKEditor加载后自动卸载的原因问询
为啥开启React严格模式后CKEditor会加载又卸载?
我来帮你拆解这个问题的根源,再给你靠谱的修复方案:
问题到底出在哪?
当你把Next.js的reactStrictMode设为true时,开发环境下React会搞个特殊操作:它会把组件的挂载→卸载流程跑两遍!目的是帮你揪出那些没正确清理的副作用(比如没销毁的定时器、订阅)。
看你的代码,你是在useEffect里用require加载CKEditor模块,然后存在editorRef里,接着把editorLoaded设为true。第一次挂载时,编辑器正常加载显示;但紧接着React模拟卸载(严格模式的检测逻辑),然后又重新挂载组件。这时候editorRef里已经有之前加载的模块了,useEffect因为依赖是空数组不会再执行,直接渲染<CKEditor>——但CKEditor的实例在第一次卸载时已经被销毁了,第二次挂载时用缓存的模块初始化,就会出现“加载完立刻卸载”的诡异现象,本质是严格模式的双重挂载和你的模块加载逻辑没适配好导致的。
另外,直接用require动态加载的方式,在Next.js的SSR环境下也容易有兼容性问题,这也是雪上加霜的一点。
怎么修复?
给你两个实用的方案,选哪个都行:
方案1:用ES动态import(官方推荐)
把require换成await import(),既能适配Next.js的模块系统,又能处理严格模式的双重挂载:
import { useState, useEffect } from 'react'; export default function CkEditorPage() { const [editorLoaded, setEditorLoaded] = useState(false); const [CKEditor, setCKEditor] = useState(null); const [ClassicEditor, setClassicEditor] = useState(null); useEffect(() => { async function loadEditorModules() { // 动态加载CKEditor组件和编辑器核心 const ckEditorModule = await import('@ckeditor/ckeditor5-react'); const classicEditorModule = await import('@ckeditor/ckeditor5-build-classic'); setCKEditor(ckEditorModule.CKEditor); setClassicEditor(classicEditorModule.default); setEditorLoaded(true); } loadEditorModules(); // 组件卸载时重置状态,避免严格模式下的残留问题 return () => { setEditorLoaded(false); setCKEditor(null); setClassicEditor(null); }; }, []); return ( <div className='w-full h-full' > <h1>Ckeditor5 Nextjs</h1> {editorLoaded && CKEditor && ClassicEditor ? ( <CKEditor className="mt-3 wrap-ckeditor" editor={ClassicEditor} onReady={editor => { console.log('编辑器准备就绪:', editor); }} /> ) : "加载中..."} </div> ); }
方案2:调整ref和状态逻辑
如果你不想改加载方式,也可以调整现有代码,确保严格模式下能正确初始化:
import { useState, useEffect, useRef } from 'react'; export default function CkEditorPage() { const editorRef = useRef({ CKEditor: null, ClassicEditor: null }); const [editorLoaded, setEditorLoaded] = useState(false); useEffect(() => { // 只有模块没加载的时候才去加载 if (!editorRef.current.CKEditor) { editorRef.current = { CKEditor: require('@ckeditor/ckeditor5-react').CKEditor, ClassicEditor: require('@ckeditor/ckeditor5-build-classic') }; } setEditorLoaded(true); // 清理状态,避免残留 return () => { setEditorLoaded(false); }; }, []); const { CKEditor, ClassicEditor } = editorRef.current; return ( <div className='w-full h-full' > <h1>Ckeditor5 Nextjs</h1> {editorLoaded && CKEditor && ClassicEditor ? ( <CKEditor className="mt-3 wrap-ckeditor" editor={ClassicEditor} onReady={editor => { console.log('编辑器准备就绪:', editor); }} /> ) : "加载中..."} </div> ); }
额外提一嘴
- 严格模式的双重挂载只在开发环境有,生产环境不会触发,所以修复后线上不会有问题。
- CKEditor官方其实更推荐用动态import的方式在Next.js里集成,因为Next.js的SSR会对直接引入的客户端组件有兼容性问题,动态import能确保编辑器只在客户端加载,避免SSR报错。
内容的提问来源于stack exchange,提问作者mahdi tajik
相关产品推荐
相关产品推荐

