Cómo detectar la pulsación de la tecla Esc en React y cómo manejarlo

126

¿Cómo detecto la pulsación de la tecla Esc en reactjs? Lo similar a jquery

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

Una vez detectado, quiero pasar la información a los componentes. Tengo 3 componentes de los cuales el último componente activo debe reaccionar a la pulsación de la tecla Escape.

Estaba pensando en una especie de registro cuando un componente se activa

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

y en todos los componentes

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

Creo que esto funcionará, pero creo que será más como una devolución de llamada. ¿Hay alguna forma mejor de manejar esto?

Neo
fuente
¿No tiene una implementación de flujo para almacenar cuál es el componente activo en lugar de hacerlo de esta manera?
Ben Hare
@BenHare: Todavía soy bastante nuevo en eso. Estoy investigando la viabilidad de migrar a React. No he probado el flux, por lo que sugiere que debería buscar en el flux la solución. PD: ¿Qué tal si se detecta la pulsación de la tecla ESC?
Neo
5
Solo para aclarar, ¿está buscando la detección de pulsaciones de teclas en el nivel del documento, como el primer bloque de código? ¿O es esto para un campo de entrada? Puedes hacer cualquiera de las dos, pero no estoy seguro de cómo formular una respuesta. Aquí hay un nivel de documento rápido, por ejemplo: codepen.io/anon/pen/ZOzaPW
Brad Colthurst
El nivel del documento estaría bien :)
Neo

Respuestas:

229

Si está buscando un manejo de eventos clave a nivel de documento, vincularlo durante componentDidMountes la mejor manera (como lo muestra el ejemplo de 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/>
    )
  }
}

Tenga en cuenta que debe asegurarse de eliminar el detector de eventos clave al desmontar para evitar posibles errores y pérdidas de memoria.

EDITAR: Si está usando ganchos, puede usar esta useEffectestructura para producir un efecto similar:

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
fuente
7
¿Cuál es el tercer argumento en addEventListenery removeEventListener?
jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/… - pueden ser opciones o useCapture
Julio Saito
2
@jhuang es el argumento useCapture que especifica si se capturan o no eventos que normalmente no aparecen comofocus . No es relevante para keydown, pero solía no ser opcional, por lo que generalmente se especifica para compatibilidad con IE <9.
Chase Sandmann
Tuve que ajustarme this.escFunctiona una función como function() { this.escFunction(event) }, de lo contrario, se estaba ejecutando al cargar la página sin ninguna "tecla abajo".
M3RS
2
No es necesario incluir "falso" como tercer argumento. Es falso por defecto.
NattyC
25

Querrás escuchar el escape keyCode(27) de 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} />
    )
  }
})

El CodePen de Brad Colthurst publicado en los comentarios de la pregunta es útil para encontrar códigos clave para otras claves.

pirt
fuente
15
Creo que vale la pena agregar que React parece no disparar el onKeyPressevento cuando la tecla que se presiona es ESC y el foco está adentro <input type="text" />. En ese escenario, puede usar onKeyDowny onKeyUp, ambos disparan, y en el orden correcto.
Tom
12

Otra forma de lograr esto en un componente funcional, es usar useEffecty useFunction, así:

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

En lugar de console.log, puedes usar useStatepara activar algo.

Jonas Sandstedt
fuente
7

React usa SyntheticKeyboardEvent para envolver el evento del navegador nativo y este evento Synthetic proporciona un atributo clave con nombre ,
que puede usar así:

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
fuente
3
diversión hecho, si usted necesita para apoyar IE9 y / o Firefox 36 o por debajo, e.keypara Escaperegresa como Esc#triggered
aug
4

Para una solución de gancho React reutilizable

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

Uso:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Bjorn Reppen
fuente
3
La solución no permite cambiar la función onEscape. Debe usarMemo y pasar onEscape como la matriz de dep para el useEffect
Neo