小编典典

React Hooks的按键/启动事件无法正常工作

reactjs

我正在尝试为我正在开发的游戏创建基于箭头的键盘控件。当然,我试图与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/

这似乎很好…


阅读 324

收藏
2020-07-22

共1个答案

小编典典

要使其像预期的类组件一样正常工作,有3个关键的事情要做。

正如其他人提到的那样,useEffect您需要添加一个[]as作为依赖项数组,该数组将仅在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));

第三件事是onKeyDownonKeyUp事件的定义已移入其中,useEffect因此您无需使用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- depsuseEffect,是因为我不想重新连接的事件,每一次一次pressedpressedKeys阵列正在发生变化。

我希望这有帮助!

2020-07-22