React中弹窗关闭后按钮无法永久禁用的问题排查
问题原因分析
你遇到的问题核心是组件重新渲染导致DOM状态丢失:
- 点击「Ask the expert」按钮后,你调用了
setPopupModalOpen(true),触发组件重新渲染,此时原来的按钮被卸载,页面只显示弹窗组件。 - 当弹窗关闭,
setPopupModalOpen(false)再次触发渲染,组件会重新生成一个全新的「Ask the expert」按钮——之前通过event.currentTarget.disabled = true设置的DOM级禁用状态,在新的按钮元素上不存在,所以按钮又恢复了可用状态。 - 反观「Fifty Fifty」按钮,它点击后没有触发
PopupModalOpen的状态变化,组件不会重新渲染,原来的按钮DOM元素一直存在,所以禁用状态能保留。
可行解决方案
虽然你提到不想用State,但React是声明式框架,直接操作DOM的状态在组件重新渲染时必然会被覆盖,以下是两种思路:
方案1:用组件内部状态控制禁用(推荐)
添加一个状态变量isExpertDisabled,用来控制按钮的禁用状态:
const [isExpertDisabled, setIsExpertDisabled] = React.useState(false); const expertClickHandler = async (event) => { // ... 原有API请求代码 ... setPopupModalOpen(true); setIsExpertDisabled(true); // 设置禁用状态 } // 渲染按钮时绑定disabled属性 <button className="game-btn" id="ask-the-expert" onClick={expertClickHandler} disabled={isExpertDisabled} > Ask the expert </button>
这种方式完全符合React的声明式逻辑,无论组件怎么重新渲染,按钮的禁用状态都会由isExpertDisabled控制,不会丢失。
方案2:用Ref结合useEffect(替代思路)
用Ref保存按钮元素,搭配状态标记点击行为,在组件渲染后同步禁用状态:
const expertBtnRef = React.useRef(null); const [isExpertClicked, setIsExpertClicked] = React.useState(false); const expertClickHandler = async (event) => { // ... 原有API请求代码 ... setPopupModalOpen(true); setIsExpertClicked(true); // 标记已点击 } React.useEffect(() => { if (isExpertClicked && expertBtnRef.current) { expertBtnRef.current.disabled = true; } }, [isExpertClicked, PopupModalOpen]); // 弹窗关闭后重新渲染时触发 // 按钮绑定ref <button className="game-btn" id="ask-the-expert" onClick={expertClickHandler} ref={expertBtnRef} > Ask the expert </button>
本质上还是需要状态标记点击行为,因为Ref本身无法触发组件重新渲染,所以这种方式不如方案1简洁直接。
内容的提问来源于stack exchange,提问作者Soham Mafidar
相关产品推荐
相关产品推荐

