在reactjs中聆听文档的按键

ff29svar  于 2022-12-29  发布在  React
关注(0)|答案(7)|浏览(179)

我想绑定以关闭escape上的active react bootstrap弹出窗口。

_handleEscKey: function(event) {
  console.log(event);
  if (event.keyCode == 27) {
    this.state.activePopover.hide();
  }
},

componentWillMount: function() {
  BannerDataStore.addChangeListener(this._onchange);
  document.addEventListener("click", this._handleDocumentClick, false);
  document.addEventListener("keyPress", this._handleEscKey, false);
},

componentWillUnmount: function() {
  BannerDataStore.removeChangeListener(this._onchange);
  document.removeEventListener("click", this._handleDocumentClick, false);
  document.removeEventListener("keyPress", this._handleEscKey, false);
},

但是当我按下任何键时,控制台上都没有记录任何东西。我也试着在Windows上听过,并在不同的情况下使用“keypress”,“keyup”等,但似乎我做错了什么。

xkftehaa

xkftehaa1#

您应该使用keydown,而不是keypress
Keypress(已弃用)通常仅用于生成字符输出的键,如文档所示
Keypress (deprecated)
当按下某个键并且该键通常生成字符值时,将激发keypress事件
Keydown
按下某个键时将激发keydown事件。

nhaq1z21

nhaq1z212#

我自己也遇到过类似的问题。我将使用您的代码来说明修复方法。

// for other devs who might not know keyCodes
var ESCAPE_KEY = 27;

_handleKeyDown = (event) => {
    switch( event.keyCode ) {
        case ESCAPE_KEY:
            this.state.activePopover.hide();
            break;
        default: 
            break;
    }
},

// componentWillMount deprecated in React 16.3
componentDidMount(){
    BannerDataStore.addChangeListener(this._onchange);
    document.addEventListener("click", this._handleDocumentClick, false);
    document.addEventListener("keydown", this._handleKeyDown);
},

componentWillUnmount() {
    BannerDataStore.removeChangeListener(this._onchange);
    document.removeEventListener("click", this._handleDocumentClick, false);
    document.removeEventListener("keydown", this._handleKeyDown);
},

由于您使用createClass的方式来执行操作,因此不需要绑定到某些方法,因为this在每个定义的方法中都是隐式的。
有一个正在运行的jsfiddle,它使用React组件创建here.的createClass方法

1zmg4dgp

1zmg4dgp3#

如果可以使用ReactHooks,那么useEffect是一个好方法,这样事件侦听器将只订阅一次,并在卸载组件时正确地取消订阅。
以下示例摘自https://usehooks.com/useEventListener/

// Hook
function useEventListener(eventName, handler, element = window){
  // Create a ref that stores handler
  const savedHandler = useRef();

  // Update ref.current value if handler changes.
  // This allows our effect below to always get latest handler ...
  // ... without us needing to pass it in effect deps array ...
  // ... and potentially cause effect to re-run every render.
  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(
    () => {
      // Make sure element supports addEventListener
      // On 
      const isSupported = element && element.addEventListener;
      if (!isSupported) return;

      // Create event listener that calls handler function stored in ref
      const eventListener = event => savedHandler.current(event);

      // Add event listener
      element.addEventListener(eventName, eventListener);

      // Remove event listener on cleanup
      return () => {
        element.removeEventListener(eventName, eventListener);
      };
    },
    [eventName, element] // Re-run if eventName or element changes
  );
};

您也可以从npm安装它,例如npm i @use-it/event-listener-请参阅此处的项目-https://github.com/donavon/use-event-listener
然后,在你的组件中使用它,你只需要在你的函数组件中调用它,传递事件名和处理程序。例如,如果你想在每次按下Escape键时都调用console.log

import useEventListener from '@use-it/event-listener'

const ESCAPE_KEYS = ['27', 'Escape'];

const App = () => {
  function handler({ key }) {
    if (ESCAPE_KEYS.includes(String(key))) {
      console.log('Escape key pressed!');
    }
  }

  useEventListener('keydown', handler);

  return <span>hello world</span>;
}
unguejic

unguejic4#

Jt oso的答案的一个版本与这个问题更相关,我认为这比其他使用外部库或API钩子来绑定/解除绑定监听器的答案简单得多。

var KEY_ESCAPE = 27;
...
    function handleKeyDown(event) {
        if (event.keyCode === KEY_ESCAPE) {
            /* do your action here */
        }
    }
...
    <div onKeyDown={handleKeyDown}>
...
oknrviil

oknrviil5#

我对可制表的div也有同样的要求。
下面的代码是在对items.map((item)=〉...

<div
    tabindex="0"
    onClick={()=> update(item.id)}
    onKeyDown={()=> update(item.id)}
   >
      {renderItem(item)}
  </div>

这对我很有效!

wj8zmpe1

wj8zmpe16#

我希望有全局事件监听器,但由于使用React Portals而出现了奇怪的行为。尽管在文档内的门户模态组件上取消了事件,但事件仍然在文档元素上触发。
我转向只在 Package 整个组件树的根对象上使用事件侦听器,这里的问题是最初是主体而不是根元素被聚焦,因此一旦你聚焦树中的一个元素,事件将首先被激发。
我的解决方案是添加一个tabindex,并使用一个效果挂钩自动聚焦它。

import React from "react";

export default GlobalEventContainer = ({ children, ...props }) => {
  const rootRef = React.useRef(null);
  useEffect(() => {
    if (document.activeElement === document.body && rootContainer.current) 
      rootContainer.current.focus();
    }
  });

  return <div {...props} tabIndex="0" ref={rootRef}>{children}</div>
};
zysjyyx4

zysjyyx47#

我在尝试编写一个Wordle克隆代码、识别按键并让它们按我所期望的那样工作时,也遇到了类似的问题。

import { useCallback, useEffect, useState } from "react";

const [random, setRandom] = useState(0.0);
const [key, setKey] = useState("");

const registerKeyPress = useCallback((e) => {
  setRandom(Math.random());
  setKey(e.key);
}, []);
useEffect(() => {
  if (key === "x") console.log("do something");
  }, [random]);
useEffect(() => {
  window.addEventListener("keydown", registerKeyPress);
}, [registerKeyPress]);

我使用一个随机数来触发useEffect()函数,该函数实际上处理按键操作,因为如果按下的键与最后一次按下的键相同,则什么也不会发生(例如,在我的wordle克隆中,单词drown或任何包含两个字母的单词或使用退格键删除多个字母的单词都不起作用)。

    • 这将导致关于useEffect的dependency数组中缺少值的警告。也许有一个更合适的方法来组织钩子(如果有人知道,请注解),但警告不会导致错误。你可以用这个注解使它们安静:// eslint-disable-next-line react-hooks/exhaustive-deps**

相关问题