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

React 18升级后LexicalComposer卸载时报destroy不是函数问题排查

React 18升级后Lexical编辑器卸载时报TypeError: destroy is not a function问题排查与修复

问题描述

我开发的应用导入了一个私有NPM包,其中包含基于LexicalComposer实现的文本编辑器组件。将应用升级至React 18版本后,在Lexical编辑器组件卸载时遇到错误:TypeError: destroy is not a function。经控制台排查,怀疑错误源自Lexical的LexicalComposer实现。

已知React 18中这类错误通常和useEffect钩子的异步操作错误用法有关,想明确:

  1. Lexical是否支持React 18?
  2. 该错误的成因是什么?
  3. 如何修复?

编辑器实现代码

import React from 'react';
import { LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
import { StandardPlugins } from '@myapp/myapp-lexical-react';
import styles from './myappLexicalEditor.module.scss';
import classNames from 'classnames/bind';
import { myappLexicalEditorTheme } from './myappLexicalEditorTheme';
import { MyappLexicalEditorProps } from './myappLexicalEditor.types';

const cx = classNames.bind(styles);

/**
 * My App implementation of the Lexical Rich Text Editor
 */
const LexicalEditor = ({
    editorConfig,
    children,
    toolbars,
    readOnly,
    onEditorChange,
    editorClass,
    editorStyle,
    editorContainerClass,
    editorContainerStyle,
    editorChildren,
    toolbarClass,
    placeholder,
    plugins,
    disableHistoryPlugin,
}: MyappLexicalEditorProps) => {
    const Placeholder = placeholder || null;
    return (
        <LexicalComposer initialConfig={{ ...editorConfig, theme: { ...myappLexicalEditorTheme, ...editorConfig.theme }, editable: !readOnly }}>
            <div className={toolbarClass}>
                <>
                    {Array.isArray(toolbars) &&
                        toolbars.map((toolbar, i) => {
                            const { ToolbarComponent, props, showToolbar } = toolbar;
                            const finalShowToolbar = showToolbar !== undefined ? showToolbar : !readOnly;
                            return finalShowToolbar ? <ToolbarComponent key={i} {...props} /> : '';
                        })
                    }
                </>
            </div>
            <div>
                <div>
                    <div style={editorStyle} >
                        <RichTextPlugin
                            contentEditable={<ContentEditable />}
                            ErrorBoundary={LexicalErrorBoundary}
                            placeholder={!readOnly ? Placeholder : null}
                        />
                        {editorChildren}
                    </div>
                </div>
            </div>
            <StandardPlugins onChange={onEditorChange} disableHistory={Boolean(disableHistoryPlugin)}/>
            <>
                {plugins && plugins.map((pluginObj, i) => {
                    const { PluginComponent, props } = pluginObj;
                    return <PluginComponent key={i} {...props} />;
                })}
            </>
            {children}
        </LexicalComposer>
    );
};

export default LexicalEditor;

错误追踪信息

Uncaught TypeError: destroy is not a function
    at safelyCallDestroy (react-dom.development.js:22928:5)
    at commitHookEffectListUnmount (react-dom.development.js:23096:11)
    at commitPassiveUnmountInsideDeletedTreeOnFiber (react-dom.development.js:25094:11)
    at commitPassiveUnmountEffectsInsideOfDeletedTree_begin (react-dom.development.js:25044:5)
    at commitPassiveUnmountEffects_begin (react-dom.development.js:24952:11)
    at commitPassiveUnmountEffects (react-dom.development.js:24937:3)
    at flushPassiveEffectsImpl (react-dom.development.js:27034:3)
    at flushPassiveEffects (react-dom.development.js:26980:14)
    at commitRootImpl (react-dom.development.js:26931:5)
    at commitRoot (react-dom.development.js:26678:5)


The above error occurred in the <observerComponent> component:

    at observerComponent (webpack-internal:///./node_modules/mobx-react-lite/es/observer.js:51:73)
    at div
    at observerComponent (webpack-internal:///./node_modules/mobx-react-lite/es/observer.js:51:73)
    at _class (webpack-internal:///./frontend/js/stores/index.js:163:88)
    at I18nextWithTranslation (webpack-internal:///./node_modules/react-i18next/dist/es/withTranslation.js:28:31)
    at div
    at LexicalComposer (webpack-internal:///./node_modules/@lexical/react/LexicalComposer.dev.js:43:3)
    at w (webpack-internal:///./node_modules/@myapp/myapp-lexical-react/lib/components/MyappLexicalEditor/MyappLexicalEditor.es.js:29:172)
    at div
    at div
    at Summary (webpack-internal:///./frontend/js/components/reusableComponents/Deal/ResponseDetails/components/SummarySection/Summary/summary.js:41:25)
    at div
    at SummarySection (webpack-internal:///./frontend/js/components/reusableComponents/Deal/ResponseDetails/components/SummarySection/summarySection.js:23:32)
    at div
    at observerComponent (webpack-internal:///./node_modules/mobx-react-lite/es/observer.js:51:73)
    at _class (webpack-internal:///./frontend/js/stores/index.js:163:88)
    at I18nextWithTranslation (webpack-internal:///./node_modules/react-i18next/dist/es/withTranslation.js:28:31)
    at div
    at TabPane (webpack-internal:///./node_modules/rc-tabs/es/TabPanelList/TabPane.js:16:24)
    at div
    at div
    at TabPanelList (webpack-internal:///./node_modules/rc-tabs/es/TabPanelList/index.js:16:17)
    at div
    at Tabs (webpack-internal:///./node_modules/rc-tabs/es/Tabs.js:73:17)
    at Tabs (webpack-internal:///./node_modules/antd/es/tabs/index.js:45:17)
    at div
    at observerComponent (webpack-internal:///./node_modules/mobx-react-lite/es/observer.js:51:73)
    at I18nextWithTranslation (webpack-internal:///./node_modules/react-i18next/dist/es/withTranslation.js:28:31)
    at _class (webpack-internal:///./frontend/js/stores/index.js:163:88)
    at I18nextWithTranslation (webpack-internal:///./node_modules/react-i18next/dist/es/withTranslation.js:28:31)
    at Route (webpack-internal:///./node_modules/react-router/esm/react-router.js:648:29)
    at Switch (webpack-internal:///./node_modules/react-router/esm/react-router.js:850:29)
    at div
    at Container (webpack-internal:///./node_modules/@myapp/myapp-ui/components/container/Container.js:18:22)
    at observerComponent (webpack-internal:///./node_modules/mobx-react-lite/es/observer.js:51:73)
    at _class (webpack-internal:///./frontend/js/stores/index.js:163:88)
    at I18nextWithTranslation (webpack-internal:///./node_modules/react-i18next/dist/es/withTranslation.js:28:31)

已尝试方案:用错误边界包裹组件无效;在主应用中添加useRef并在useEffect卸载时赋值为null,未解决问题。


解答

1. Lexical对React 18的支持

Lexical从v0.3.0版本开始正式支持React 18,但v0.3.0之前的旧版本在React 18的严格模式或卸载流程中会出现兼容问题,比如你遇到的destroy is not a function错误。

2. 错误成因

这个错误的核心原因:

  • 旧版Lexical内部部分useEffect钩子返回了非函数类型的销毁逻辑(比如null),或直接返回undefined之外的无效值。React 18会严格校验useEffect的返回值——若返回值存在,必须是可调用的销毁函数,否则就会抛出该错误。
  • 私有包@myapp/myapp-lexical-react中的StandardPlugins或自定义插件也可能存在类似问题,比如插件的useEffect错误处理了销毁逻辑。

3. 修复方案

方案一:升级Lexical到兼容React 18的版本

将项目中@lexical/react和lexical核心包升级到v0.3.0及以上:

npm install lexical@^0.3.0 @lexical/react@^0.3.0

同时检查私有包@myapp/myapp-lexical-react的依赖,同步升级其内部的Lexical版本,确保依赖链完全兼容React 18。

方案二:修复自定义插件/StandardPlugins中的useEffect问题

若升级Lexical后问题仍存在,检查StandardPlugins或自定义插件的实现,确保所有useEffect的返回值要么是一个销毁函数,要么不返回任何值(即默认返回undefined)。

错误示例:

useEffect(() => {
  // 初始化逻辑
  return null; // React 18不允许返回非函数/undefined的值
});

正确示例:

// 无需销毁逻辑时,不返回任何值
useEffect(() => {
  // 初始化逻辑
});

// 需要销毁逻辑时,返回函数
useEffect(() => {
  const subscription = editor.registerListener(...);
  return () => {
    subscription.unsubscribe();
  };
}, [editor]);

方案三:临时禁用React 18严格模式(仅临时调试)

若暂时无法升级依赖,可在React 18入口文件中移除<StrictMode>,绕过React的双重挂载/卸载校验逻辑。但这只是临时方案,长期仍需升级依赖解决根本问题。


内容的提问来源于stack exchange,提问作者Con

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 16:55:55