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

React数组无法正确更新:按键事件仅保留最后一个值问题

React按键事件更新数组始终只保留最后一个元素的问题修复

问题根源

  1. 闭包状态捕获陷阱:你的useEffect依赖列表是空数组,导致updateKeypresses函数仅在组件首次渲染时创建,它内部引用的keypresses永远是初始的空数组。每次调用setKeypresses时,都是基于空数组添加新的按键码,所以数组永远只有最后一个元素。
  2. 两处语法错误:
    • window.addEventListeneder拼写错误,正确写法是window.addEventListener
    • 添加监听的对象是window,但移除时用了document,两者不匹配会导致监听无法正确移除,引发内存泄漏

修复后的代码

function Component() {
  const [keypresses, setKeypresses] = useState([]);
  
  // 使用函数式更新获取最新状态
  function updateKeypresses(e) {
    setKeypresses(prevKeypresses => [...prevKeypresses, e.keyCode]);
  }

  useEffect(() => {
    // 修正拼写错误,统一使用window管理监听
    window.addEventListener('keydown', updateKeypresses);
    return () => {
      window.removeEventListener('keydown', updateKeypresses); 
    }  
  }, []); // 空数组依赖依然有效,因为函数式更新不依赖外部状态

  useEffect(() => {
    console.log(keypresses);
  }, [keypresses])

  return <div>已按键码:{keypresses.join(', ')}</div>;
}

关键修复说明

  • 函数式更新:调用setKeypresses时传入一个函数,React会自动传入当前最新的状态prevKeypresses,确保每次更新都基于最新数组添加元素,彻底规避闭包带来的旧状态捕获问题。
  • 统一监听对象:添加和移除监听都使用window,保证监听能正确清理,避免内存泄漏。

替代方案(不推荐)

如果坚持要直接使用当前状态,可通过useCallback包裹更新函数,并将状态加入依赖列表,但这种方式会在每次状态更新时重新创建监听,效率不如函数式更新:

const updateKeypresses = useCallback((e) => {
  setKeypresses([...keypresses, e.keyCode]);
}, [keypresses]);

useEffect(() => {
  window.addEventListener('keydown', updateKeypresses);
  return () => window.removeEventListener('keydown', updateKeypresses);
}, [updateKeypresses]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:23:25