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

React中useState更新后的值无法在自定义事件监听函数中获取

问题根因

核心是React的闭包陷阱:

  • 你编写的useEffect依赖为空数组,仅会在组件首次挂载时执行1次
  • 事件绑定的onMyEvent是首次渲染生成的函数,该函数闭包捕获的是首次渲染时的var1初始值[]
  • 后续var1更新触发组件重渲染时,会生成新的onMyEvent函数,但没有解绑旧的事件监听也未绑定新函数,事件触发时始终执行首次渲染的旧函数,无法拿到更新后的var1值。
解决思路

方案1:补充useEffect依赖+清理函数

将var1和onMyEvent加入useEffect依赖数组,每次依赖变化时先解绑旧监听,再绑定新的监听,保证绑定的永远是最新的onMyEvent函数:

import { useEffect, useState, useCallback } from 'react';

function App() {
  const [var1, setVar1] = useState([])

  // 配合useCallback减少不必要的函数重建
  const onMyEvent = useCallback((e)=>{
    console.log("Detail var1 in onMyEvent: ", var1)
  }, [var1])

  useEffect(()=>{
    document.addEventListener('my_event', onMyEvent);
    setTimeout(()=>setVar1(['a','b']),1000)
    // 组件卸载/依赖变化时解绑旧监听
    return () => document.removeEventListener('my_event', onMyEvent);
  },[var1, onMyEvent]) // 补充依赖

  const doSomething = ()=>{
    document.dispatchEvent(new CustomEvent('my_event', { detail: 5 }));
  }
  return (
    <div className="App">
      <div>{var1.toString()}</div><br/>
      <button onClick={doSomething}>Show Data</button>
    </div>
  );
}

export default App;

方案2:用useRef存储最新状态

useRef的current属性是可变引用,不会被闭包捕获旧值,也不需要调整事件绑定逻辑:

import { useEffect, useState, useRef } from 'react';

function App() {
  const [var1, setVar1] = useState([])
  // 新建ref同步var1的最新值
  const var1Ref = useRef(var1)
  // var1更新时同步刷新ref的值
  useEffect(() => {
    var1Ref.current = var1
  }, [var1])

  const onMyEvent = (e)=>{
    // 从ref读取最新值
    console.log("Detail var1 in onMyEvent: ", var1Ref.current)
  }
  useEffect(()=>{
    document.addEventListener('my_event', onMyEvent);
    setTimeout(()=>setVar1(['a','b']),1000)
    return () => document.removeEventListener('my_event', onMyEvent);
  },[])
  const doSomething = ()=>{
    document.dispatchEvent(new CustomEvent('my_event', { detail: 5 }));
  }
  return (
    <div className="App">
      <div>{var1.toString()}</div><br/>
      <button onClick={doSomething}>Show Data</button>
    </div>
  );
}

export default App;

方案3:使用useEvent(React18+正式支持)

useEvent是官方专门为解决这类闭包问题提供的API,包装后的函数可以始终读取到最新的state,不需要额外添加依赖:

import { useEffect, useState, useEvent } from 'react';

function App() {
  const [var1, setVar1] = useState([])
  // useEvent包装的回调永远能拿到最新state
  const onMyEvent = useEvent((e)=>{
    console.log("Detail var1 in onMyEvent: ", var1)
  })
  useEffect(()=>{
    document.addEventListener('my_event', onMyEvent);
    setTimeout(()=>setVar1(['a','b']),1000)
    return () => document.removeEventListener('my_event', onMyEvent);
  },[])
  const doSomething = ()=>{
    document.dispatchEvent(new CustomEvent('my_event', { detail: 5 }));
  }
  return (
    <div className="App">
      <div>{var1.toString()}</div><br/>
      <button onClick={doSomething}>Show Data</button>
    </div>
  );
}

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 22:15:00