我正在尝试为我正在开发的游戏创建基于箭头的键盘控件。当然,我正在尝试与 React 保持同步,所以我想创建一个函数组件并使用钩子(Hook)。我创建了一个 JSFiddle对于我的 buggy 组件。
它几乎按预期工作,除非我同时按下很多箭头键。然后似乎没有触发某些 keyup
事件。也可能是“状态”未正确更新。
我喜欢这样:
const ALLOWED_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']
const [pressed, setPressed] = React.useState([])
const handleKeyDown = React.useCallback(event => {
const { key } = event
if (ALLOWED_KEYS.includes(key) && !pressed.includes(key)) {
setPressed([...pressed, key])
}
}, [pressed])
const handleKeyUp = React.useCallback(event => {
const { key } = event
setPressed(pressed.filter(k => k !== key))
}, [pressed])
React.useEffect(() => {
document.addEventListener('keydown', handleKeyDown)
document.addEventListener('keyup', handleKeyUp)
return () => {
document.removeEventListener('keydown', handleKeyDown)
document.removeEventListener('keyup', handleKeyUp)
}
})
我认为我做的是正确的,但作为 hooks 的新手,很可能这就是问题所在。特别是因为我重新创建了与基于类的组件相同的组件: https://jsfiddle.net/vus4nrfe/
这似乎工作正常......
最佳答案
要使它像您的类组件一样按预期工作,需要做 3 件关键的事情。
正如其他人针对 useEffect
提到的那样,您需要添加一个 []
作为依赖项数组,它只会在 addEventLister
函数中触发一次。
第二个主要问题是您没有像在类组件中那样改变功能组件中的 pressed
数组的previous state,如下所示:
// onKeyDown event
this.setState(prevState => ({
pressed: [...prevState.pressed, key],
}))
// onKeyUp event
this.setState(prevState => ({
pressed: prevState.pressed.filter(k => k !== key),
}))
您需要按以下方式更新功能之一:
// onKeyDown event
setPressedKeys(previousPressedKeys => [...previousPressedKeys, key]);
// onKeyUp event
setPressedKeys(previousPressedKeys => previousPressedKeys.filter(k => k !== key));
第三件事是 onKeyDown
和 onKeyUp
事件的定义已经移到 useEffect
中,所以你不需要使用 useCallback
。
上面提到的事情解决了我这边的问题。请找到我制作的以下工作 GitHub 存储库,它按预期工作:
https://github.com/norbitrial/react-keydown-useeffect-componentdidmount
如果您更喜欢这里,可以找到一个可用的 JSFiddle 版本:
essential part来自存储库,完全工作的组件:
const KeyDownFunctional = () => {
const [pressedKeys, setPressedKeys] = useState([]);
useEffect(() => {
const onKeyDown = ({key}) => {
if (Consts.ALLOWED_KEYS.includes(key) && !pressedKeys.includes(key)) {
setPressedKeys(previousPressedKeys => [...previousPressedKeys, key]);
}
}
const onKeyUp = ({key}) => {
if (Consts.ALLOWED_KEYS.includes(key)) {
setPressedKeys(previousPressedKeys => previousPressedKeys.filter(k => k !== key));
}
}
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
return () => {
document.removeEventListener('keydown', onKeyDown);
document.removeEventListener('keyup', onKeyUp);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return <>
<h3>KeyDown Functional Component</h3>
<h4>Pressed Keys:</h4>
{pressedKeys.map(e => <span key={e} className="key">{e}</span>)}
</>
}
我为 useEffect
使用 //eslint-disable-next-line react-hooks/exhaustive-deps
的原因是因为我不想在 pressed
或 pressedKeys
数组发生变化时,每次都重新附加事件。
希望对您有所帮助!
关于javascript - React Hooks 的 Keydown/up 事件无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59546928/