在 reactjs 中收听文档的按键

IT技术 reactjs react-jsx react-bootstrap
2021-04-17 16:50:59

我想绑定以在escape按下时关闭 active react bootstrap popover。这是代码

_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);
   },

但是当我按任意键时,控制台中没有任何记录。我也试过在窗口上听不同的情况。'keypress','keyup' 等,但似乎我做错了什么。

6个回答

你应该使用keydown而不是keypress.

Keypress(已弃用)通常仅用于根据文档产生字符输出的键

按键(已弃用)

当一个键被按下并且该键通常产生一个字符值时会触发 keypress 事件

按键

当按下某个键时会触发 keydown 事件。

按键已被弃用。
2021-06-01 16:50:59

我自己也有类似的问题。我将使用您的代码来说明修复程序。

// 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 组件创建的 createClass 方法

@Steven10172 好点,由于构造函数并未真正在 React.createClass 方法中定义,因此您始终可以在 getInitialState() 中进行绑定。
2021-05-25 16:50:59
关于上面的评论,这是一个很好的例子,说明在哪里绑定和使用事件监听器stackoverflow.com/questions/32553158/...
2021-06-05 16:50:59
请注意,componentWillMount自 React 16.3 起已弃用。IMO 你应该在componentDidMount.
2021-06-07 16:50:59
由于每次绑定都会提供一个新实例,这将无法正确删除事件侦听器。确保缓存绑定返回的结果以从文档中正确添加和删除
2021-06-08 16:50:59
重要提示:确保这两项都在最高级别完成:1) handleKeyDown 函数定义和 2) 侦听器设置。它对我不起作用,因为我在我的一个子组件中做了这两件事。当我将它移动到主要组件并且它起作用时。
2021-06-20 16:50:59

如果你可以使用 React Hooks,一个很好的方法是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

然后,要在您的组件中使用它,您只需在您的功能组件中调用它并传递事件名称和处理程序。例如,如果您想console.log每次按下 Escape 键时:

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>;
}
感谢您发布此信息,帮助我修复了全局键盘处理程序中的大量内存泄漏。FWIW,“将侦听器保存到ref”效果真的很关键 - 不要在useEffect将它们添加到依赖项数组中传递您的事件处理程序document.body.onKeyDown
2021-05-26 16:50:59
任何试图在 NextJS 上实现它的人,只需在useEventListener方法中添加一个窗口对象验证检查以确保它在客户端运行。并删除 param elementif (typeof window === 'undefined') return; const element = window;
2021-05-28 16:50:59
@aendrew:与将处理程序保存到 ref 并仅声明一个函数有什么区别?
2021-06-11 16:50:59
@thelonglqd 我想是因为否则它们会被多次添加为事件处理程序——不过不要引用我的话,那是半年前的事了,我的记忆模糊了!!
2021-06-11 16:50:59
如果 App 不是功能组件,则无法使用它
2021-06-12 16:50:59

与这个问题更相关的 Jt oso 答案的一个版本。我认为这比使用外部库或 API 挂钩绑定/取消绑定侦听器的其他答案要简单得多。

var KEY_ESCAPE = 27;
...
    function handleKeyDown(event) {
        if (event.keyCode === KEY_ESCAPE) {
            /* do your action here */
        }
    }
...
    <div onKeyDown={handleKeyDown}>
...
你实际上可以使用 if (event.key === 'Escape')
2021-05-23 16:50:59
该项目必须首先聚焦。如果您想要一个全局事件侦听器,它可能不会被触发,因为最初主体元素是焦点。
2021-06-05 16:50:59

我对可选项卡的 div 有相同的要求。

以下代码对我来说是在调用 items.map((item)=> ...

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

这对我有用!