Como detectar a tecla Esc no React e como lidar com isso

124

Como detecto pressionar a tecla Esc nos reactjs? A mesma coisa que jquery

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

Uma vez detectado, quero passar as informações aos componentes. Eu tenho três componentes, dos quais o último componente ativo precisa reagir ao pressionamento da tecla Escape.

Eu estava pensando em um tipo de registro quando um componente se torna ativo

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

e em todos os 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)}/>
    )
  }
}

Acredito que isso funcionará, mas acho que será mais como um retorno de chamada. Existe alguma maneira melhor de lidar com isso?

Neo
fonte
Você não tem uma implementação de fluxo para armazenar qual é o componente ativo em vez de fazê-lo dessa maneira?
Ben Hare
@ BenHare: Eu ainda sou muito novo nisso. Estou analisando a viabilidade de migrar para o React. Eu não tentei o fluxo, então você está sugerindo que eu deva procurar no fluxo a solução. PS: Que tal detectar a tecla ESC pressionada?
Neo
5
Apenas para esclarecer, você está procurando a detecção de pressionamento de tecla no nível do documento, como o primeiro bloco de código? Ou isso é para um campo de entrada? Você pode fazer isso, mas não tenho certeza de como definir uma resposta. Aqui está um rápido nível de documento, por exemplo: codepen.io/anon/pen/ZOzaPW
Brad Colthurst
Nível do documento seria bom :)
Neo

Respostas:

228

Se você estiver procurando por uma manipulação de eventos de chave no nível do documento , vinculá- la durante componentDidMounté a melhor maneira (como mostra o exemplo 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/>
    )
  }
}

Observe que você deve remover o ouvinte de eventos principal ao desmontar para evitar possíveis erros e vazamentos de memória.

Edição: Se você estiver usando ganchos, você pode usar esta useEffectestrutura para produzir um efeito semelhante:

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
fonte
6
Qual é o terceiro argumento em addEventListenere removeEventListener?
jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/... - pode ser otions OU useCapture
Julio Saito
1
@jhuang é o argumento useCapture que especifica se deve ou não capturar eventos que geralmente não surgemfocus . Não é relevante para keydown, mas costumava não ser opcional, por isso é geralmente especificado para compatibilidade com o IE <9.
precisa
Eu tive que quebrar this.escFunctionem uma função como function() { this.escFunction(event) }, caso contrário, ele estava sendo executado no carregamento da página sem nenhum "pressionamento de tecla".
M3RS 12/12
1
Não há necessidade de incluir 'false' como o terceiro argumento. É falso por padrão.
NattyC 19/07/19
25

Você vai querer ouvir escape keyCode(27) do 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} />
    )
  }
})

O CodePen de Brad Colthurst publicado nos comentários da pergunta é útil para encontrar códigos de chave para outras chaves.

pirt
fonte
15
Acho que vale a pena acrescentar que o React parece não disparar o onKeyPressevento quando a tecla pressionada é ESC e o foco está dentro <input type="text" />. Nesse cenário, você pode usar onKeyDowne onKeyUp, os quais disparam, e na ordem correta.
Tom
12

Outra maneira de conseguir isso em um componente funcional é usar useEffecte useFunction, assim:

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

Em vez de console.log, você pode usar useStatepara disparar algo.

Jonas Sandstedt
fonte
7

React usa SyntheticKeyboardEvent para quebrar o evento nativo do navegador e esse evento Synthetic fornece o atributo key nomeado ,
que você pode usar assim:

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
fonte
3
divertido fato, se você precisa para apoiar IE9 e / ou Firefox 36 ou abaixo, e.keypor Escapevolta como Esc#triggered
agosto
4

Para uma solução reutilizável de gancho 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

Uso:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Bjorn Reppen
fonte
3
A solução não permite alterar a função onEscape. Você deve usar o Memo e passar o onEscape como o array dep para o useEffect
Neo