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

React Piano组件状态更新异常:需重新保存才生效

解决React Piano组件useEffect状态更新不生效及热刷新重复打印问题

问题现象

  • 将钢琴创建逻辑从组件return语句移至useEffect后,在useEffect内设置test状态为'hello',但按键触发打印时始终显示状态初始默认值
  • 修改代码触发热刷新后,按键会同时打印旧代码的默认值和新代码的预期值'hello'

问题根源

这是React常见的闭包陷阱导致的:

  • 首次渲染时,useEffect内创建的钢琴按键监听函数捕获了当时的test状态(初始默认值),后续状态更新后,旧的监听函数依然持有旧状态的引用,不会自动更新
  • 热刷新时旧组件实例未被完全销毁,新旧实例的事件监听同时存在,因此会出现重复打印的情况

解决方案

方案1:用useRef同步最新状态

通过useRef保存状态的最新值,让监听函数能访问到实时状态:

// 初始化状态和ref
const [test, setTest] = useState('default');
const testRef = useRef(test);

// 同步状态到ref
useEffect(() => {
  testRef.current = test;
}, [test]);

// 创建钢琴实例的useEffect
useEffect(() => {
  setTest('hello');
  const piano = new Piano(/* 你的配置参数 */);
  // 按键时打印ref的当前值
  piano.on('keydown', () => {
    console.log(testRef.current);
  });
  // 清理函数:销毁钢琴实例,避免内存泄漏和重复监听
  return () => {
    piano.destroy();
  };
}, []);

方案2:让useEffect依赖状态,重新创建实例

把状态加入useEffect的依赖数组,当状态更新时重新创建钢琴实例,确保监听函数拿到最新状态:

const [test, setTest] = useState('default');

// 单独设置状态的useEffect
useEffect(() => {
  setTest('hello');
}, []);

// 依赖test的useEffect,状态更新时重新创建钢琴
useEffect(() => {
  const piano = new Piano(/* 你的配置参数 */);
  piano.on('keydown', () => {
    console.log(test);
  });
  return () => {
    piano.destroy();
  };
}, [test]); // 加入test作为依赖

热刷新问题修复

无论哪种方案,都必须确保useEffect的清理函数正确销毁旧的钢琴实例和事件监听,这样热刷新时旧实例的监听会被移除,不会出现重复打印。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:08:23