Jak wykryć naciśnięcie klawisza Esc w Reakcie i jak sobie z tym poradzić

126

Jak wykryć naciśnięcie klawisza Esc na reagjach? Podobnie jak w jquery

$(document).keyup(function(e) {
     if (e.keyCode == 27) { // escape key maps to keycode `27`
        // <DO YOUR WORK HERE>
    }
});

Po wykryciu chcę przekazać komponenty informacji. Mam 3 komponenty, z których ostatni aktywny składnik musi reagować na naciśnięcie klawisza Escape.

Myślałem o czymś w rodzaju rejestracji, kiedy składnik staje się aktywny

class Layout extends React.Component {
  onActive(escFunction){
    this.escFunction = escFunction;
  }
  onEscPress(){
   if(_.isFunction(this.escFunction)){
      this.escFunction()
   }
  }
  render(){
    return (
      <div class="root">
        <ActionPanel onActive={this.onActive.bind(this)}/>
        <DataPanel onActive={this.onActive.bind(this)}/>
        <ResultPanel onActive={this.onActive.bind(this)}/>
      </div>
    )
  }
}

i na wszystkich komponentach

class ActionPanel extends React.Component {
  escFunction(){
   //Do whatever when esc is pressed
  }
  onActive(){
    this.props.onActive(this.escFunction.bind(this));
  }
  render(){
    return (   
      <input onKeyDown={this.onActive.bind(this)}/>
    )
  }
}

Wierzę, że to zadziała, ale myślę, że będzie to bardziej jak oddzwonienie. Czy jest lepszy sposób na rozwiązanie tego problemu?

Neo
źródło
Czy nie masz do zapisania implementacji strumienia, która jest aktywnym składnikiem, zamiast robić to w ten sposób?
Ben Hare
@BenHare: Nadal jestem w tym całkiem nowy. Sprawdzam możliwość migracji do React. Nie próbowałem flux, więc sugerujesz, że powinienem przyjrzeć się flux w celu znalezienia rozwiązania. PS: A co z wykrywaniem naciśnięcia klawisza ESC?
Neo
5
Dla wyjaśnienia, czy szukasz funkcji wykrywania naciśnięć klawiszy na poziomie dokumentu, takich jak pierwszy blok kodu? Czy jest to dla pola wejściowego? Możesz to zrobić, po prostu nie jestem pewien, jak sformułować odpowiedź. Oto szybki poziom dokumentu, na przykład: codepen.io/anon/pen/ZOzaPW
Brad Colthurst
Poziom dokumentu byłby w porządku :)
Neo

Odpowiedzi:

229

Jeśli szukasz obsługi zdarzeń kluczowych na poziomie dokumentu, componentDidMountnajlepszym sposobem jest powiązanie go w trakcie (jak pokazano na przykładzie kodowania Brada Colthursta ):

class ActionPanel extends React.Component {
  constructor(props){
    super(props);
    this.escFunction = this.escFunction.bind(this);
  }
  escFunction(event){
    if(event.keyCode === 27) {
      //Do whatever when esc is pressed
    }
  }
  componentDidMount(){
    document.addEventListener("keydown", this.escFunction, false);
  }
  componentWillUnmount(){
    document.removeEventListener("keydown", this.escFunction, false);
  }
  render(){
    return (   
      <input/>
    )
  }
}

Należy pamiętać, że podczas odmontowywania należy usunąć detektor zdarzeń kluczy, aby zapobiec potencjalnym błędom i wyciekom pamięci.

EDYCJA: Jeśli używasz haków, możesz użyć tej useEffectstruktury, aby uzyskać podobny efekt:

const ActionPanel = (props) => {
  const escFunction = useCallback((event) => {
    if(event.keyCode === 27) {
      //Do whatever when esc is pressed
    }
  }, []);

  useEffect(() => {
    document.addEventListener("keydown", escFunction, false);

    return () => {
      document.removeEventListener("keydown", escFunction, false);
    };
  }, []);

  return (   
    <input />
  )
};
Chase Sandmann
źródło
7
Jaki jest trzeci argument w addEventListeneri removeEventListener?
jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/ ... - czy możesz użyć funkcji Capture
Julio Saito
2
@jhuang to argument useCapture, który określa, czy przechwytywać zdarzenia, które zwykle nie pojawiają się jakfocus . Nie ma to znaczenia dla keydown, ale kiedyś nie było opcjonalne, więc zwykle jest określone dla zgodności z IE <9.
Chase Sandmann,
Musiałem zawinąć this.escFunctionw funkcję taką jak function() { this.escFunction(event) }, w przeciwnym razie byłaby wykonywana przy ładowaniu strony bez żadnego "keydown".
M3RS
2
Nie ma potrzeby dodawania „fałsz” jako trzeciego argumentu. Domyślnie jest fałszywe.
NattyC,
25

Będziesz chciał posłuchać ucieczki keyCode(27) z ReactSyntheticKeyBoardEvent onKeyDown :

const EscapeListen = React.createClass({
  handleKeyDown: function(e) {
    if (e.keyCode === 27) {
      console.log('You pressed the escape key!')
    }
  },

  render: function() {
    return (
      <input type='text'
             onKeyDown={this.handleKeyDown} />
    )
  }
})

CodePen Brada Colthursta zamieszczone w komentarzach do pytania jest pomocne w znajdowaniu kodów kluczy dla innych kluczy.

pirt
źródło
15
Myślę, że warto dodać, że React wydaje się nie uruchamiać onKeyPresszdarzenia, gdy naciśnięty klawisz to ESC, a fokus jest w środku <input type="text" />. W tym scenariuszu możesz użyć onKeyDowni onKeyUp, które strzelają, i we właściwej kolejności.
Tom
12

Innym sposobem osiągnięcia tego w komponencie funkcjonalnym jest użycie useEffecti useFunction, na przykład:

import React, { useEffect } from 'react';

const App = () => {


  useEffect(() => {
    const handleEsc = (event) => {
       if (event.keyCode === 27) {
        console.log('Close')
      }
    };
    window.addEventListener('keydown', handleEsc);

    return () => {
      window.removeEventListener('keydown', handleEsc);
    };
  }, []);

  return(<p>Press ESC to console log "Close"</p>);
}

Zamiast console.log możesz użyć useStatedo wyzwolenia czegoś.

Jonas Sandstedt
źródło
7

React używa SyntheticKeyboardEvent do zawijania natywnego zdarzenia przeglądarki, a to zdarzenie Synthetic dostarcza nazwany atrybut klucza ,
którego możesz użyć w następujący sposób:

handleOnKeyDown = (e) => {
  if (['Enter', 'ArrowRight', 'Tab'].includes(e.key)) {
    // select item
    e.preventDefault();
  } else if (e.key === 'ArrowUp') {
    // go to top item
    e.preventDefault();
  } else if (e.key === 'ArrowDown') {
    // go to bottom item
    e.preventDefault();
  } else if (e.key === 'Escape') {
    // escape
    e.preventDefault();
  }
};
BitOfUniverse
źródło
3
Fakt, zabawa, jeśli trzeba wspierać IE9 i / lub Firefox 36 lub poniżej, e.keydla Escapewraca jako Esc#triggered
Aug
4

Do haczyka wielokrotnego użytku React

import React, { useEffect } from 'react';

const useEscape = (onEscape) => {
    useEffect(() => {
        const handleEsc = (event) => {
            if (event.keyCode === 27) 
                onEscape();
        };
        window.addEventListener('keydown', handleEsc);

        return () => {
            window.removeEventListener('keydown', handleEsc);
        };
    }, []);
}

export default useEscape

Stosowanie:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Bjorn Reppen
źródło
3
Rozwiązanie nie pozwala na zmianę funkcji onEscape. Powinieneś użyćMemo i przekazać onEscape jako tablicę dep dla useEffect
Neo