TinyMCE工具栏按钮onAction调用setState时状态异常重置问题
解决TinyMCE工具栏按钮点击重置React状态的问题
嘿,这个问题我太熟悉了!本质上是TinyMCE的setup函数闭包捕获了旧的React状态/函数引用导致的,咱们来拆解原因并给出解决方案:
问题根源分析
外部按钮能正常工作,是因为它每次都会随着React组件重新渲染,绑定的是最新的addTextBlock函数——这个函数能访问到当前最新的textBlocks状态。
但TinyMCE的setup函数只在编辑器初始化时执行一次,这时候它捕获的addTextBlock是组件第一次渲染时的版本,里面的textBlocks还是初始的空数组。当你第二次点击工具栏按钮时,调用的依然是这个旧函数,它会基于空数组去添加新块,看起来就像是把状态重置了。
解决方案1:用函数式更新状态(最简单有效)
把你的状态更新逻辑改成函数式更新,这样React会自动传入最新的状态值,不用依赖闭包里的旧引用:
假设你原来的addTextBlock是这样的:
const [textBlocks, setTextBlocks] = useState([]); const addTextBlock = () => { // 这里的textBlocks是闭包捕获的旧值 setTextBlocks([...textBlocks, { id: Date.now(), content: "" }]); };
改成函数式更新:
const addTextBlock = () => { // prevBlocks是React提供的最新状态 setTextBlocks(prevBlocks => [...prevBlocks, { id: Date.now(), content: "" }]); };
这样不管addTextBlock是旧函数还是新函数,都能拿到最新的状态来更新,不会再出现重置的问题。
解决方案2:用useRef保存最新的函数引用(兜底方案)
如果函数式更新不适用你的场景,你可以用useRef来保存最新的addTextBlock引用,确保TinyMCE按钮每次调用的都是最新版本:
import { useRef, useState } from 'react'; const YourComponent = () => { const [textBlocks, setTextBlocks] = useState([]); const addTextBlockRef = useRef(); const addTextBlock = () => { setTextBlocks([...textBlocks, { id: Date.now(), content: "" }]); }; // 每次渲染都更新ref的current值,指向最新的addTextBlock addTextBlockRef.current = addTextBlock; const editorConfig = { setup: (editor) => { editor.ui.registry.addButton("AddNew", { text: "Add new", onAction: () => addTextBlockRef.current() // 调用最新的函数 }); }, // 其他TinyMCE配置... }; return ( // 你的组件内容 ); };
为什么外部按钮没问题?
外部按钮是React组件的一部分,每次状态更新后组件会重新渲染,按钮的onClick会重新绑定到最新的addTextBlock函数,自然能拿到最新的状态。而TinyMCE的工具栏按钮是在编辑器初始化时创建的,不会随着React组件重新渲染而更新绑定,所以才会出现闭包陷阱。
内容的提问来源于stack exchange,提问作者PauloZapo
相关产品推荐
相关产品推荐

