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

React Hotkeys无法获取组件state值的问题求助

问题描述

按下键盘按键时,希望使用useState声明的变量的当前值执行操作,但按键触发的方法始终获取state的默认值(0),即使组件中的test值已经更新。

重现代码

import React from 'react';
import { HotKeys } from 'react-hotkeys';
import { useCallback, useEffect, useState } from 'react';

const keyMap = {
    MOVE_DOWN: 'a'
};

export function App(props) {
  const [test, setTest] = useState(0);

  const focusNextRow = useCallback(() => {
      console.log("test", test);
      setTest(test => test + 1);
      console.log("test2", test);
  }, [test]);

  useEffect(() => {
      console.log("TEST IS NOW ", test);
  }, [test]);

  const handlers = {
      MOVE_DOWN: focusNextRow
  };

  return (
    <div className='App'>
      <HotKeys keyMap={keyMap} handlers={handlers}>
        <div>test</div>
      </HotKeys>
    </div>
  );
}

运行现象

每次按下a键,控制台输出test 0、test2 0,但useEffect中能正确打印更新后的test值(如TEST IS NOW 1、TEST IS NOW 2等)。


解决方法

方法1:用useRef保存最新state值

通过useRef创建引用同步最新state,绕过闭包陷阱直接获取当前值:

import React from 'react';
import { HotKeys } from 'react-hotkeys';
import { useCallback, useEffect, useRef, useState } from 'react';

const keyMap = {
    MOVE_DOWN: 'a'
};

export function App(props) {
  const [test, setTest] = useState(0);
  const testRef = useRef(test);

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

  const focusNextRow = useCallback(() => {
      // 从ref中获取最新值
      console.log("test", testRef.current);
      setTest(prev => prev + 1);
      console.log("test2", testRef.current);
  }, []);

  useEffect(() => {
      console.log("TEST IS NOW ", test);
  }, [test]);

  const handlers = {
      MOVE_DOWN: focusNextRow
  };

  return (
    <div className='App'>
      <HotKeys keyMap={keyMap} handlers={handlers}>
        <div>test</div>
      </HotKeys>
    </div>
  );
}

方法2:确保HotKeys感知handlers变化

让handlers每次渲染都生成新引用,确保HotKeys绑定最新的处理函数:

import React from 'react';
import { HotKeys } from 'react-hotkeys';
import { useCallback, useEffect, useState } from 'react';

const keyMap = {
    MOVE_DOWN: 'a'
};

export function App(props) {
  const [test, setTest] = useState(0);

  const focusNextRow = useCallback(() => {
      console.log("test", test);
      setTest(prev => prev + 1);
      console.log("test2", test);
  }, [test]);

  useEffect(() => {
      console.log("TEST IS NOW ", test);
  }, [test]);

  // 每次渲染生成新的handlers对象,确保HotKeys拿到最新函数
  const handlers = {
      MOVE_DOWN: focusNextRow
  };

  return (
    <div className='App'>
      <HotKeys keyMap={keyMap} handlers={handlers}>
        <div>test</div>
      </HotKeys>
    </div>
  );
}

方法3:函数式更新state(仅需更新场景)

如果只是需要依赖最新值更新state,直接用setTest的函数式写法即可,无需直接访问state变量:

const focusNextRow = useCallback(() => {
    setTest(prev => {
        console.log("当前test值", prev);
        return prev + 1;
    });
}, []);

原因说明

问题核心是闭包陷阱:focusNextRow通过useCallback缓存时,捕获了创建时的test值。若HotKeys未及时更新绑定的handler,就会始终调用旧版本的函数,导致获取的test永远是初始值0。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 09:25:03