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

React中TinyMCE自定义按钮动态构建菜单项问题求助

React版TinyMCE动态构建自定义菜单按钮菜单项问题

我用React版TinyMCE组件加载编辑器,已经实现了文档里的自定义菜单项示例,但想动态构建自定义菜单按钮的菜单项。根据文档说明,fetch函数可以在菜单每次打开时构建菜单项,我尝试把fetch指向外部的buildCustomEditorButtons函数来实现动态构建,但遇到了语法或实现问题。

现有可运行示例代码

<Editor
    apiKey='123'
    onChange={(e) => this.handleDirty(e)}
    onInit={(evt, editor) => this.editorRef.current = editor}
    initialValue={this.renderInputValue('body')}
    init={{
        height: 600,
        menubar: true,
        plugins: [
            'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
            'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
            'insertdatetime', 'media', 'table', 'code', 'help', 'wordcount'
        ],
        toolbar: 'undo redo | blocks | ' +
            'bold italic forecolor | alignleft aligncenter ' +
            'alignright alignjustify | bullist numlist outdent indent | ' +
            'removeformat | insertvarbutton help',
        setup: function (editor) {
            /* Menu items are recreated when the menu is closed and opened, so we need
               a variable to store the toggle menu item state. */
            var toggleState = false;

            /* example, adding a toolbar menu button */
            editor.ui.registry.addMenuButton('insertvarbutton', {
                text: 'Insert Variable',
                fetch: function (callback) {
                    let test = []
                    let items = [
                        {
                            type: 'menuitem',
                            text: 'Menu item 1',
                            onAction: function () {
                                editor.insertContent('&nbsp;<em>You clicked menu item 1!</em>');
                            }
                        },
                        {
                            type: 'nestedmenuitem',
                            text: 'Menu item 2',
                            icon: 'user',
                            getSubmenuItems: function () {
                                return [
                                    {
                                        type: 'menuitem',
                                        text: 'Sub menu item 1',
                                        icon: 'unlock',
                                        onAction: function () {
                                            editor.insertContent('&nbsp;<em>You clicked Sub menu item 1!</em>');
                                        }
                                    },
                                    {
                                        type: 'menuitem',
                                        text: 'Sub menu item 2',
                                        icon: 'lock',
                                        onAction: function () {
                                            editor.insertContent('&nbsp;<em>You clicked Sub menu item 2!</em>');
                                        }
                                    }
                                ];
                            }
                        }
                    ];
                    callback(items);
                }
            });

        },
        content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }'
    }}
/>

期望的实现方式

setup: function (editor) {
    /* Menu items are recreated when the menu is closed and opened, so we need
       a variable to store the toggle menu item state. */
    var toggleState = false;

    /* example, adding a toolbar menu button */
    editor.ui.registry.addMenuButton('insertvarbutton', {
        text: 'Insert Variable',
        fetch: this.buildCustomEditorButtons(editor)
    });

},

外部构建函数代码

buildCustomEditorButtons(editor) {
    let test = []
    for (const [key, value] of Object.entries(this.state.templateVars.system_vars)) {
        test << {
            type: "nestedmenuitem",
            text: key,
            getSubmenuItems: function () {
                value.map((i) => {
                    return {
                        type: 'menuitem',
                        text: i.name,
                        onAction: function () {
                            editor.insertContent(i.value);
                        }
                    }
                })
            }
        }
    }
    return(test)
}

问题分析与修复方案

你的代码存在几个关键问题,逐个修复即可:

  1. fetch函数的调用方式错误
    TinyMCE的fetch需要接收一个函数,而你直接执行了this.buildCustomEditorButtons(editor),相当于把函数的返回值传给了fetch,不符合要求。需要让buildCustomEditorButtons返回一个接受callback参数的函数,或者在fetch内部调用它并传入callback。

  2. 数组添加元素的语法错误
    JavaScript中没有test << { ... }的数组添加语法,应该使用test.push({ ... })。

  3. map函数返回值未处理
    value.map(...)生成了新的菜单项数组,但你没有在getSubmenuItems中返回这个数组,导致子菜单无法正常渲染。

  4. this指向问题
    setup如果用普通函数定义,内部的this不会指向React组件实例,需要改用箭头函数来确保this指向正确。

修复方案一:让构建函数返回符合要求的fetch函数

buildCustomEditorButtons(editor) {
    // 返回一个接收callback参数的函数,适配TinyMCE的fetch要求
    return (callback) => {
        let test = [];
        for (const [key, value] of Object.entries(this.state.templateVars.system_vars)) {
            test.push({
                type: "nestedmenuitem",
                text: key,
                getSubmenuItems: () => {
                    // 返回map生成的子菜单项数组
                    return value.map((i) => ({
                        type: 'menuitem',
                        text: i.name,
                        onAction: () => {
                            editor.insertContent(i.value);
                        }
                    }));
                }
            });
        }
        callback(test);
    };
}

对应的setup配置(用箭头函数确保this指向组件实例):

setup: (editor) => {
    var toggleState = false;

    editor.ui.registry.addMenuButton('insertvarbutton', {
        text: 'Insert Variable',
        fetch: this.buildCustomEditorButtons(editor)
    });
},

修复方案二:在fetch内部直接调用构建函数

setup: (editor) => {
    var toggleState = false;

    editor.ui.registry.addMenuButton('insertvarbutton', {
        text: 'Insert Variable',
        fetch: (callback) => {
            const items = this.buildCustomEditorButtons(editor);
            callback(items);
        }
    });
},

此时对应的构建函数可以直接返回菜单项数组:

buildCustomEditorButtons(editor) {
    let test = [];
    for (const [key, value] of Object.entries(this.state.templateVars.system_vars)) {
        test.push({
            type: "nestedmenuitem",
            text: key,
            getSubmenuItems: () => {
                return value.map((i) => ({
                    type: 'menuitem',
                    text: i.name,
                    onAction: () => {
                        editor.insertContent(i.value);
                    }
                }));
            }
        });
    }
    return test;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:10:04