Erkennen des Esc-Tastendrucks in React und Umgang damit

124

Wie erkenne ich den Esc-Tastendruck bei Reaktionen? Ähnlich wie bei jquery

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

Einmal erkannt, möchte ich die Info-Komponenten weitergeben. Ich habe 3 Komponenten, von denen die letzte aktive Komponente auf den Escape-Tastendruck reagieren muss.

Ich dachte an eine Art Registrierung, wenn eine Komponente aktiv wird

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

und auf allen Komponenten

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

Ich glaube, das wird funktionieren, aber ich denke, es wird eher ein Rückruf sein. Gibt es einen besseren Weg, um damit umzugehen?

Neo
quelle
Haben Sie keine Flussimplementierung zum Speichern der aktiven Komponente, anstatt dies auf diese Weise zu tun?
Ben Hare
@ BenHare: Ich bin noch ziemlich neu darin. Ich prüfe die Machbarkeit einer Migration nach React. Ich habe kein Flussmittel ausprobiert, daher schlagen Sie vor, dass ich das Flussmittel nach der Lösung untersuchen sollte. PS: Wie wäre es mit einem ESC-Tastendruck?
Neo
5
Suchen Sie zur Verdeutlichung nach einer Tastendruckerkennung auf Dokumentebene wie dem ersten Codeblock? Oder ist das für ein Eingabefeld? Sie können es auch tun, ich bin mir nur nicht sicher, wie ich eine Antwort formulieren soll. Hier ist eine kurze
Brad Colthurst
Dokumentebene wäre in Ordnung :)
Neo

Antworten:

227

Wenn Sie nach einer Schlüsselereignisbehandlung auf Dokumentebene suchen, componentDidMountist es am besten, diese während zu binden (wie im Codepen-Beispiel von Brad Colthurst gezeigt ):

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

Beachten Sie, dass Sie sicherstellen sollten, dass der Listener für Schlüsselereignisse beim Aufheben der Bereitstellung entfernt wird, um mögliche Fehler und Speicherlecks zu vermeiden.

BEARBEITEN: Wenn Sie Hooks verwenden, können Sie diese useEffectStruktur verwenden, um einen ähnlichen Effekt zu erzielen:

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
quelle
6
Was ist das dritte Argument in addEventListenerund removeEventListener?
Jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/… - kann otions ou useCapture sein
Julio Saito
1
@jhuang Es ist das useCapture-Argument, das angibt, ob Ereignisse erfasst werden sollen , die normalerweisefocus nicht in die Luft sprudeln . Es ist nicht relevant für keydown, aber es war früher nicht optional, daher wird es normalerweise aus Gründen der Kompatibilität mit IE <9 angegeben.
Chase Sandmann
Ich musste mich this.escFunctionin eine Funktion wie einwickelnfunction() { this.escFunction(event) } einschließen, sonst wurde sie beim Laden der Seite ohne "Keydown" ausgeführt.
M3RS
1
Es ist nicht erforderlich, 'false' als drittes Argument anzugeben. Es ist standardmäßig falsch.
NattyC
25

Sie sollten auf Flucht keyCode(27) aus der Reaktion hörenSyntheticKeyBoardEvent 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} />
    )
  }
})

Der in den Kommentaren der Frage veröffentlichte CodePen von Brad Colthurst ist hilfreich, um Schlüsselcodes für andere Schlüssel zu finden.

pirt
quelle
15
Ich denke, es lohnt sich hinzuzufügen, dass React das Ereignis anscheinend nicht auslöst, onKeyPresswenn die gedrückte Taste ESC ist und der Fokus im Inneren liegt <input type="text" />. In diesem Szenario können Sie onKeyDownund onKeyUp, beide feuern, in der richtigen Reihenfolge verwenden.
Tom
12

Eine andere Möglichkeit , dies in einer funktionalen Komponente zu erreichen, ist zu verwenden useEffectund useFunction, wie folgt aus :

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

Anstelle von console.log können Sie useStateetwas auslösen.

Jonas Sandstedt
quelle
7

React verwendet SyntheticKeyboardEvent , um das native Browserereignis zu verpacken, und dieses Synthetic-Ereignis bietet ein benanntes Schlüsselattribut ,
das Sie wie folgt verwenden können:

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
quelle
3
wenn Sie Spaß Tatsache unterstützen müssen IE9 und / oder 36 Firefox oder unten, e.keyfür Escapekommt zurück als Esc#triggered
August
4

Für eine wiederverwendbare React Hook-Lösung

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

Verwendung:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Björn Reppen
quelle
3
Die Lösung erlaubt keine Änderung der onEscape-Funktion. Sie sollten Memo verwenden und onEscape als dep-Array für useEffect
Neo