javascript - React Hooks 的 Keydown/up 事件无法正常工作

标签 javascript reactjs react-hooks keyboard-events

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

它几乎按预期工作,除非我同时按下很多箭头键。然后似乎没有触发某些 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)
    }
  })

我认为我做的是正确的,但作为 hooks 的新手,很可能这就是问题所在。特别是因为我重新创建了与基于类的组件相同的组件: https://jsfiddle.net/vus4nrfe/

这似乎工作正常......

最佳答案

要使它像您的类组件一样按预期工作,需要做 3 件关键的事情。

正如其他人针对 useEffect 提到的那样,您需要添加一个 [] 作为依赖项数组,它只会在 addEventLister 函数中触发一次。

第二个主要问题是您没有像在类组件中那样改变功能组件中的 pressed 数组的previous state,如下所示:

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

essential part来自存储库,完全工作的组件:

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 数组发生变化时,每次都重新附加事件。

希望对您有所帮助!

关于javascript - React Hooks 的 Keydown/up 事件无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59546928/

相关文章:

javascript - 如何使用 javascript 将文本替换为链接?

javascript - 如何使用 JavaScript 中的按钮单击将值传递给 Angular 元素 Web 组件?

reactjs - 如何在不使用react-stripe-elements中的 <CardElement/> 的情况下使 stripe.createToken() 工作?

javascript - 如何使用 React 功能 Hook 在异步操作后获取更改的状态

reactjs - 我无法获取 localStorage 数据,我正在使用 useEffect Hook

javascript - 删除节点元素而不删除innerHTML

javascript - 如何获得链接到新页面的按钮,并预先填充搜索栏

javascript - react hook useState不能存储json或string

react native useState 错误 ("is read-only")

reactjs - 使用 useState() 钩子(Hook)测试功能组件时设置状态