React Hooks 的 Keydown/up 事件无法正常工作 [英] Keydown/up events with React Hooks not working properly

查看:50
本文介绍了React Hooks 的 Keydown/up 事件无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我正在开发的游戏创建基于箭头的键盘控件.当然,我正在努力与 React 保持同步,所以我想创建一个函数组件并使用钩子.我为我的错误组件创建了一个 JSFiddle.

I'm trying to create arrow based keyboard controls for a game I'm working on. Of course I'm trying to stay up to date with React so I wanted to create a function component and use hooks. I've created a JSFiddle for my buggy component.

它几乎按预期工作,除非我同时按下很多箭头键.然后似乎没有触发某些 keyup 事件.也可能是状态"没有正确更新.

It's almost working as expected, except when I press a lot of the arrow keys at the same time. Then it seems like some keyup events aren't triggered. It could also be that the 'state' is not updated properly.

我喜欢这个:

  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/

I have the idea that I'm doing it correctly, but being new to hooks it is very likely that this is where the problem is. Especially since I've re-created the same component as a class based component: https://jsfiddle.net/vus4nrfe/

这似乎工作正常...

推荐答案

要使其像您的类组件一样按预期工作,需要做 3 件事.

There are 3 key things to do to make it work as expected just like your class component.

正如其他人在 useEffect 中提到的,您需要添加一个 [] 作为依赖数组,该数组只会触发一次 addEventLister 函数.

As others mentioned for useEffect you need to add an [] as a dependency array which will trigger only once the addEventLister functions.

第二个主要问题是,您没有像在类组件中那样在功能组件中改变 <​​code>pressed 数组的先前状态,如下所示:

The second thing which is the main issue is that you are not mutating the pressed array's previous state in functional component as you did in class component, just like below:

// onKeyDown event
this.setState(prevState => ({
   pressed: [...prevState.pressed, key],
}))

// onKeyUp event
this.setState(prevState => ({
   pressed: prevState.pressed.filter(k => k !== key),
}))

您需要按以下方式更新功能一:

You need to update in functional one as the following:

// onKeyDown event
setPressedKeys(previousPressedKeys => [...previousPressedKeys, key]);

// onKeyUp event
setPressedKeys(previousPressedKeys => previousPressedKeys.filter(k => k !== key));

第三件事是 onKeyDownonKeyUp 事件的定义已经移动到 useEffect 内部,所以你不需要使用 useCallback.

The third thing is that the definition of the onKeyDown and onKeyUp events have been moved inside of useEffect so you don't need to use useCallback.

提到的事情解决了我的问题.请找到我制作的以下工作 GitHub 存储库,它按预期工作:

The mentioned things solved the issue on my end. Please find the following working GitHub repository what I've made which works as expected:

https://github.com/norbitrial/react-keydown-useeffect-componentdidmount

如果您更喜欢这里的 JSFiddle 版本,请在此处找到它:

Find a working JSFiddle version if you like it better here:

https://jsfiddle.net/0aogqbyp/

基本部分来自存储库,完全工作的组件:

The essential part from the repository, fully working component:

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 的原因是因为我不想要一旦 pressedpressedKeys 数组发生变化,每次都重新附加事件.

The reason why I'm using // eslint-disable-next-line react-hooks/exhaustive-deps for the useEffect is because I don't want to reattach the events every single time once the pressed or pressedKeys array is changing.

我希望这会有所帮助!

这篇关于React Hooks 的 Keydown/up 事件无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆