反应寄存器同时设置状态(钩子)

cub*_*fox 3 javascript reactjs react-hooks

我有一个应该监听多个按键事件的组件。

 const [activeKeys, setActiveKeys] = useState([]);

  const onKeyDown = e => {
    if (!activeKeys.includes(e.key)) {
      console.log("keydown", e.key);
      setActiveKeys([...activeKeys, e.key]);
    }
  };
  const onKeyUp = e => {
    console.log("keyup", e.key);
    setActiveKeys([...activeKeys].filter(i => i !== e.key));
  };

  useEffect(() => {
    document.addEventListener("keydown", onKeyDown);
    document.addEventListener("keyup", onKeyUp);

    return () => {
      document.removeEventListener("keydown", onKeyDown);
      document.removeEventListener("keyup", onKeyUp);
    };
  });
Run Code Online (Sandbox Code Playgroud)

是核心。完整代码可以在这里找到(codesandbox)

90% 的情况下它都可以正常工作,但是:

当同时释放 2 个键时,状态不会更新两次。

(您可以尝试在上面提供的代码和框中重现它,同时按任意 2 个键,然后同时释放它们。可能需要几次尝试才能解决问题,这是发生问题的 gif,这就是它的外观就像(数字是按下的键的数量,“s”是应该按下的键,但是,您可以在控制台日志中看到“s”的按键已注册。) 错误的屏幕截图

以前有人遇到过类似的事情吗?据我所知,问题不在于

  • 事件监听器(console.log 被触发)

  • 重新渲染(您可以尝试按其他一些键,该键将保留在数组中)

这就是为什么我假设问题出在钩子上,但是我无法解释为什么会发生这种情况。

cbd*_*per 6

问题是您正在使用 的简单形式setState(newValue)和 ,它用新值替换您的状态。您必须使用 的函数形式,setState( (prevState) => {} );因为您的新状态取决于先前的状态。

尝试这个:

const onKeyDown = e => {
    if (!activeKeys.includes(e.key)) {
      console.log("keydown", e.key, activeKeys);
      setActiveKeys(prevActiveKeys => [...prevActiveKeys, e.key]);
    }
  };
  const onKeyUp = e => {
    console.log("keyup", e.key, activeKeys);
    setActiveKeys(prevActiveKeys =>
      [...prevActiveKeys].filter(i => i !== e.key)
    );
  };
Run Code Online (Sandbox Code Playgroud)

链接到沙盒