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

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

问题描述

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

这似乎很好...

推荐答案

要使其像预期的类组件一样正常工作,需要做三件事.

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.

第二个主要问题是,您没有像在类组件中那样更改功能组件中的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),
}))

您需要在功能1中进行以下更新:

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>)}
    </>
}

之所以将// eslint-disable-next-line react-hooks/exhaustive-deps用作useEffect的原因是,当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的按键/启动事件无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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