我正在尝试为我正在开发的游戏创建基于箭头的键盘控件。当然,我试图与React保持同步,所以我想创建一个功能组件并使用钩子。我为越野车组件创建了一个JSFiddle。
几乎可以按预期运行,除非同时按下很多箭头键。然后似乎keyup没有触发某些事件。也可能是“状态”未正确更新。
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) } })
这似乎很好…
要使其像预期的类组件一样正常工作,有3个关键的事情要做。
正如其他人提到的那样,useEffect您需要添加一个[]as作为依赖项数组,该数组将仅在addEventLister函数触发一次。
useEffect
[]
addEventLister
第二个主要问题是,您没有像在类组件中那样改变功能组件中pressed数组的 先前状态 ,如下所示:
pressed
// 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。
onKeyDown
onKeyUp
useCallback
上面提到的事情最终解决了这个问题。请找到我所做的以下工作GitHub存储库,它们可以按预期工作:
https://github.com/norbitrial/react-keydown-useeffect- componentdidmount
如果您更喜欢JSFiddle版本,请在这里找到它:
https://jsfiddle.net/0aogqbyp/
来自存储库的基本部分,完全正常工作的组件:
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>)} </> }
为什么我使用的原因// eslint-disable-next-line react-hooks/exhaustive- deps了useEffect,是因为我不想重新连接的事件,每一次一次pressed或pressedKeys阵列正在发生变化。
// eslint-disable-next-line react-hooks/exhaustive- deps
pressedKeys
我希望这有帮助!