Как обнаружить нажатие клавиши Esc в React и как с этим справиться

126

Как определить нажатие клавиши Esc на reactjs? То же, что и jquery

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

После обнаружения я хочу передать информацию компонентам. У меня есть 3 компонента, из которых последний активный компонент должен реагировать на нажатие клавиши выхода.

Я думал о некой регистрации, когда компонент становится активным

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

и по всем компонентам

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

Я верю, что это сработает, но я думаю, что это будет больше похоже на обратный вызов. Есть ли лучший способ справиться с этим?

Нео
источник
У вас нет реализации потока для хранения активного компонента вместо того, чтобы делать это таким образом?
Бен Хэйр,
@BenHare: Я все еще новичок в этом. Я изучаю возможность перехода на React. Я не пробовал флюс, поэтому вы предлагаете мне изучить флюс для решения. PS: Как насчет обнаружения нажатия клавиши ESC?
Нео
5
Чтобы уточнить, вы ищете обнаружение нажатия клавиш на уровне документа, например, первый блок кода? Или это для поля ввода? Вы можете сделать то же самое, я просто не знаю, как сформулировать ответ. Вот, например, быстрый уровень документа: codepen.io/anon/pen/ZOzaPW
Брэд Колтерст,
Уровень документа был бы в порядке :)
Neo

Ответы:

229

Если вы ищете обработку ключевых событий на уровне документа, то componentDidMountлучшим способом будет привязка ее во время (как показано в примере кода Брэда Колтерста ):

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

Обратите внимание, что вы должны обязательно удалить прослушиватель ключевых событий при размонтировании, чтобы предотвратить возможные ошибки и утечки памяти.

РЕДАКТИРОВАТЬ: если вы используете хуки, вы можете использовать эту useEffectструктуру для получения аналогичного эффекта:

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 />
  )
};
Чейз Сандманн
источник
7
Каков третий аргумент в addEventListenerи removeEventListener?
jhuang
2
@jhuang developer.mozilla.org/en-US/docs/Web/API/EventTarget/… - можно использовать Capture
Хулио Сайто,
2
@jhuang - это аргумент useCapture, который указывает, следует ли захватывать события, которые обычно не всплываютfocus . Это не актуально для keydown, но раньше не было необязательным, поэтому обычно указывается для совместимости с IE <9.
Chase Sandmann
Мне пришлось обернуть this.escFunctionфункцию вроде function() { this.escFunction(event) }, иначе она выполнялась при загрузке страницы без каких-либо «нажатий».
M3RS
2
Нет необходимости включать «ложь» в качестве третьего аргумента. По умолчанию это false.
NattyC
25

Вы захотите послушать escape keyCode(27) из 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 Брэда Колтерста, размещенный в комментариях к вопросу, полезен для поиска ключевых кодов для других ключей.

ПИВТ
источник
15
Я думаю, стоит добавить, что React, похоже, не запускает onKeyPressсобытие, когда нажата клавиша ESC, а фокус находится внутри <input type="text" />. В этом сценарии вы можете использовать onKeyDownи onKeyUp, оба срабатывают, и в правильном порядке.
Tom
12

Другой способ сделать это в функциональном компоненте - использовать useEffectи useFunction, например:

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

Вместо console.log вы можете использовать useStateдля запуска чего-либо.

Йонас Сандштедт
источник
7

React использует SyntheticKeyboardEvent для обертывания собственного события браузера, и это синтетическое событие предоставляет именованный ключевой атрибут ,
который вы можете использовать следующим образом:

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
источник
3
Интересный факт, если вам нужно поддерживать IE9 и / или Firefox 36 или ниже, e.keyибо Escapeвозвращается как Esc#triggered
авг,
4

Для многоразового решения с крючком 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

Использование:

const [isOpen, setIsOpen] = useState(false);
useEscape(() => setIsOpen(false))
Бьорн Реппен
источник
3
Решение не позволяет изменять функцию onEscape. Вы должны использоватьMemo и передать onEscape в качестве массива dep для useEffect
Neo