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 被触发)
重新渲染(您可以尝试按其他一些键,该键将保留在数组中)
这就是为什么我假设问题出在钩子上,但是我无法解释为什么会发生这种情况。
问题是您正在使用 的简单形式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)