Comment détecter la touche Echap dans React et comment la gérer

126

Comment détecter la pression de la touche Echap sur reactjs? La chose similaire à jquery

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

Une fois détecté, je veux transmettre les composants d'informations. J'ai 3 composants dont le dernier composant actif doit réagir à la pression de la touche d'échappement.

Je pensais à une sorte d'enregistrement lorsqu'un composant devient actif

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

et sur tous les composants

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

Je crois que cela fonctionnera, mais je pense que ce sera plus comme un rappel. Y a-t-il une meilleure façon de gérer cela?

Néo
la source
N'avez-vous pas d'implémentation de flux pour stocker quel est le composant actif au lieu de le faire de cette façon?
Ben Hare
@BenHare: Je suis encore assez nouveau dans ce domaine. J'étudie la faisabilité de migrer vers React. Je n'ai pas essayé le flux, vous suggérez donc que je devrais rechercher la solution dans le flux. PS: Que diriez-vous de détecter la pression d'une touche ESC?
Neo
5
Juste pour clarifier, recherchez-vous une détection de pression de touche au niveau du document, comme le premier bloc de code? Ou est-ce pour un champ de saisie? Vous pouvez faire l'un ou l'autre, je ne sais tout simplement pas comment formuler une réponse. Voici un rapide document au niveau par exemple: codepen.io/anon/pen/ZOzaPW
Brad Colthurst
Le niveau du document irait bien :)
Neo

Réponses:

229

Si vous recherchez une gestion des événements de clé au niveau du document, componentDidMountla meilleure méthode est de la lier pendant (comme le montre l'exemple codepen de Brad Colthurst ):

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

Notez que vous devez vous assurer de supprimer l'écouteur d'événements de clé lors du démontage pour éviter les erreurs potentielles et les fuites de mémoire.

EDIT: Si vous utilisez des hooks, vous pouvez utiliser cette useEffectstructure pour produire un effet similaire:

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
la source
7
Quel est le troisième argument de addEventListeneret removeEventListener?
jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/… - peut être otions ou useCapture
Julio Saito
2
@jhuang c'est l'argument useCapture qui spécifie s'il faut ou non capturer des événements qui généralement ne bouillonnent pas commefocus . Ce n'est pas pertinent pour keydown, mais il n'était pas facultatif , il est donc généralement spécifié pour la compatibilité avec IE <9.
Chase Sandmann
J'ai dû envelopper this.escFunctiondans une fonction comme function() { this.escFunction(event) }, sinon il s'exécutait au chargement de la page sans aucun "keydown".
M3RS
2
Pas besoin d'inclure «faux» comme troisième argument. C'est faux par défaut.
NattyC
25

Vous voudrez écouter les évasions keyCode(27) du 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} />
    )
  }
})

Le CodePen de Brad Colthurst publié dans les commentaires de la question est utile pour trouver des codes clés pour d'autres clés.

pirt
la source
15
Je pense qu'il vaut la peine d'ajouter que React semble ne pas déclencher l' onKeyPressévénement lorsque la touche enfoncée est ESC et que le focus est à l'intérieur <input type="text" />. Dans ce scénario, vous pouvez utiliser onKeyDownet onKeyUp, qui se déclenchent tous les deux et dans le bon ordre.
Tom
12

Une autre façon d'accomplir cela dans un composant fonctionnel est d'utiliser useEffectet useFunction, comme ceci:

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

Au lieu de console.log, vous pouvez utiliser useStatepour déclencher quelque chose.

Jonas Sandstedt
la source
7

React utilise SyntheticKeyboardEvent pour encapsuler l'événement de navigateur natif et cet événement Synthetic fournit un attribut de clé nommé ,
que vous pouvez utiliser comme ceci:

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
la source
3
amusant fait , si vous avez besoin de soutenir IE9 et / ou Firefox 36 ou ci - dessous, e.keypour Escaperevient comme Esc#triggered
août
4

Pour une solution de crochet React réutilisable

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

Usage:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Bjorn Reppen
la source
3
La solution ne permet pas de changer de fonction onEscape. Vous devez utiliserMemo et passer onEscape comme tableau dep pour useEffect
Neo