React自定义全局事件触发时无法更新isToSave状态问题咨询
问题原因
- 全局事件回调修改状态后未同步更新ref值
你只有在handleOnClick中手动同步了myStateRef.current和isToSave的状态,但是通过set-is-to-save事件回调调用setisToSave修改状态后,没有同步更新myStateRef.current,导致后续事件触发时读取的ref永远是旧值。 - 事件解绑逻辑无效
你addEventListener和removeEventListener传入的是两个不同的匿名函数实例,浏览器无法匹配到要移除的监听器,不仅会导致组件卸载时监听器残留造成内存泄漏,多次渲染后还会重复绑定同一事件,出现状态更新异常。 - 存在闭包陷阱风险
即使使用了ref规避部分闭包问题,但事件回调依赖的逻辑没有和状态更新同步,依旧会出现取值异常。
解决方案
基础修复方案(保留原有ref逻辑)
const [isToSave, setIsToSave] = React.useState(false) const myStateRef = React.useRef(isToSave); // 单独定义事件回调,保证添加和移除用的是同一个函数引用 const handleSetIsToSave = React.useCallback(() => { const newVal = !myStateRef.current setIsToSave(newVal) // 修改状态后同步更新ref,保证后续取值正确 myStateRef.current = newVal }, []) React.useEffect(() => { window.addEventListener('set-is-to-save', handleSetIsToSave, false); return () => { window.removeEventListener('set-is-to-save', handleSetIsToSave); } }, [handleSetIsToSave]) function handleOnClick(e: React.MouseEvent<HTMLDivElement>): void { const newVal = !isToSave myStateRef.current = newVal setIsToSave(newVal) }
简化优化方案(移除冗余ref)
如果没有其他场景必须用到ref存储状态,可以直接用React useState的函数式更新写法,完全规避闭包和ref同步问题,代码更简洁:
const [isToSave, setIsToSave] = React.useState(false) React.useEffect(() => { const handleSetIsToSave = () => { // 函数式更新直接拿到最新的状态值,不需要依赖闭包或者额外的ref setIsToSave(prev => !prev) } window.addEventListener('set-is-to-save', handleSetIsToSave, false); return () => { window.removeEventListener('set-is-to-save', handleSetIsToSave); } }, []) function handleOnClick(e: React.MouseEvent<HTMLDivElement>): void { setIsToSave(prev => !prev) }
内容的提问来源于stack exchange,提问作者Ghost Checker
相关产品推荐
相关产品推荐

