tho*_*nas 4 javascript keyboard-events reactjs react-hooks
我正在尝试为我正在开发的游戏创建基于箭头的键盘控件。当然,我正在努力与 React 保持同步,所以我想创建一个函数组件并使用钩子。我为我的错误组件创建了一个JSFiddle。
它几乎按预期工作,除非我同时按下很多箭头键。然后似乎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)
    }
  })
我的想法是我做对了,但是作为钩子的新手,很可能这就是问题所在。特别是因为我重新创建了与基于类的组件相同的组件:https : //jsfiddle.net/vus4nrfe/
这似乎工作正常......
有 3 个关键的事情要做才能使它像您的类组件一样按预期工作。
正如其他人提到的,useEffect您需要添加一个[]作为依赖项数组,该数组只会触发一次addEventLister函数。
第二个主要问题是您没有像在类组件中那样改变函数组件中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.
提到的事情解决了我的问题。请找到我制作的以下工作 GitHub 存储库,它按预期工作:
https://github.com/norbitrial/react-keydown-useeffect-componentdidmount
如果您更喜欢,请在此处找到可用的 JSFiddle 版本:
存储库中的基本部分,完全工作的组件:
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-depsfor 的原因useEffect是因为一旦pressedorpressedKeys数组发生变化,我不想每次都重新附加事件。
我希望这有帮助!
| 归档时间: | 
 | 
| 查看次数: | 14782 次 | 
| 最近记录: |