Reactjs'deki belge için tuşa basmayı dinleyin


86

Baskıdaki etkin tepki önyükleme açılır penceresini kapatmak için bağlanmak istiyorum.İşte escapekod

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

Ancak herhangi bir tuşa bastığımda konsolda hiçbir şey oturum açmıyor. Bunu pencerede de dinlemeye çalıştım ve farklı durumlarda. 'Keypress', 'keyup' vb.Ama sanki yanlış bir şey yapıyorum.


Ne olursa olsun, React için tüm bunları çok daha kolay hale getirmek için bir keydown kitaplığı
yayınladım

Yanıtlar:


63

Kullanmalı keydownve kullanmamalısın keypress.

Tuşa basma (kullanımdan kaldırıldı) genellikle yalnızca belgelere göre bir karakter çıkışı üreten anahtarlar için kullanılır.

Tuşa basma (kullanımdan kaldırıldı)

Tuşa basma olayı, bir tuşa basıldığında tetiklenir ve bu tuş normalde bir karakter değeri üretir

Keydown

Tuşa basma olayı, bir tuşa basıldığında tetiklenir.


1
tuşa basma kullanımdan kaldırıldı.
TimeParadox

52

Ben de bununla benzer bir sorun yaşadım. Bir düzeltmeyi göstermek için kodunuzu kullanacağım.

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

Bir şeyler yapmak için createClass yöntemini kullandığınızdan, thistanımlanan her yöntemde örtük olduğu gibi belirli yöntemlere bağlanmanız gerekmez .

Burada React bileşen oluşturmanın createClass yöntemini kullanan çalışan bir jsfiddle var.


9
Bu, her seferinde yeni bir örnek veren bağlama nedeniyle olay dinleyicisini düzgün bir şekilde kaldırmayacaktır. Belgeye düzgün bir şekilde eklemek ve belgeden çıkarmak için bağlanan sonuçları önbelleğe aldığınızdan emin olun
Steven10172

@ Steven10172 Yapıcı React.createClass yönteminde gerçekten tanımlanmadığından, her zaman getInitialState () 'de bağlanabilirsiniz.
Chris Sullivan

Yukarıdaki yorumlarla bağlantılı olarak, olay dinleyicilerini nereye bağlayıp kullanacağınıza dair güzel bir örnek stackoverflow.com/questions/32553158/…
Craig Myles

1
componentWillMountReact 16.3 itibariyle kullanımdan kaldırıldığını unutmayın . IMO yerine olay dinleyicilerini kaydetmelisiniz componentDidMount.
Igor Akkerman

24

React Hook'ları kullanabiliyorsanız, iyi bir yaklaşım, useEffectolay dinleyicisine yalnızca bir kez abone olacak ve bileşen kaldırıldığında abonelikten düzgün bir şekilde çıkacaktır.

Aşağıdaki örnek https://usehooks.com/useEventListener/ adresinden alınmıştır :

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

Örneğin npm'den de yükleyebilirsiniz npm i @use-it/event-listener- projeye buradan bakın - https://github.com/donavon/use-event-listener .

Daha sonra, onu bileşeninizde kullanmak için, olay adını ve işleyiciyi geçirerek işlevsel bileşeninizin içinde çağırmanız yeterlidir. Örneğin console.log, Escape tuşuna her basıldığında isterseniz :

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>;
}

Uygulama işlevsel bir bileşen değilse, onu kullanamaz
ashubuntu

1
Bunu yayınladığınız için teşekkürler, genel klavye işleyicilerimdeki büyük bir bellek sızıntısını gidermeme yardımcı oldu. FWIW, "dinleyicileri bir yere kaydetme ref" efekti gerçekten anahtardır - olay işleyicilerinizi, useEffectonları ekleyen bağımlılık dizisinde geçirmeyin document.body.onKeyDown!
aendrew

@aendrew: İşleyiciyi kaydetmekten bir ref ve sadece bir işlev bildirmek arasındaki fark nedir?
thelonglqd

@thelonglqd Sanırım, aksi takdirde birden çok kez olay işleyicileri olarak ekleniyorlar - benden alıntı yapmayın, bu yarım yıldan uzun bir süre önceydi ve hafızam sisli !!
aendrew

2

Jt oso'nun cevabının bu soruyla daha alakalı bir versiyonu. Bunun, dinleyiciyi bağlamak / ayırmak için harici kitaplıklar veya API kancaları kullanan diğer yanıtlardan çok daha basit olduğunu düşünüyorum.

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

4
Önce öğeye odaklanılmalıdır. Global bir olay dinleyicisine sahip olmak istiyorsanız, başlangıçta gövde öğesi odaklandığı için tetiklenmeyebilir.
n1ru4l

Aslında kullanabilirsinizif (event.key === 'Escape')
Yifan Ai

1

Sekme özellikli bir div için aynı gereksinimleri karşılıyordum.

Aşağıdaki kod benim için items.map ((item) => ... çağrısının içindeydi.

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

Bu benim için çalıştı!


1

Küresel olay dinleyicilerine sahip olmak istedim ve React Portallarını kullandığım için tuhaf davranışlar yaşadım. Etkinlik, belge içindeki bir portal kalıcı bileşeninde iptal edilmesine rağmen belge öğesinde hala tetiklendi.

Tüm bileşen ağacını saran bir kök nesnede yalnızca olay dinleyicilerini kullanmaya yöneldim. Buradaki sorun, başlangıçta kök öğeye değil vücudun odaklanmış olmasıdır, bu nedenle, ağaçtaki bir öğeye odaklandığınızda olayların ilk olarak tetiklenmesidir.

Aradığım çözüm, bir sekme dizini eklemek ve onu bir efekt kancasıyla otomatik olarak odaklamak.

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>
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.