How to detect Esc Key Press in React and how to handle it
JavascriptReactjsJavascript Problem Overview
How do I detect Esc keypress on reactjs? The similar thing to jquery
$(document).keyup(function(e) {
if (e.keyCode == 27) { // escape key maps to keycode `27`
// <DO YOUR WORK HERE>
}
});
Once detected I want to pass the info down components. I have 3 components out of which last active component needs to react to the escape key press.
I was thinking of a kind of registering when a component becomes active
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>
)
}
}
and on all the components
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)}/>
)
}
}
I believe this will work but I think it will be more like a callback. Is there any better way to handle this?
Javascript Solutions
Solution 1 - Javascript
If you're looking for a document-level key event handling, then binding it during componentDidMount
is the best way (as shown by Brad Colthurst's codepen example):
class ActionPanel extends React.Component {
constructor(props){
super(props);
this.escFunction = this.escFunction.bind(this);
}
escFunction(event){
if (event.key === "Escape") {
//Do whatever when esc is pressed
}
}
componentDidMount(){
document.addEventListener("keydown", this.escFunction, false);
}
componentWillUnmount(){
document.removeEventListener("keydown", this.escFunction, false);
}
render(){
return (
<input/>
)
}
}
Note that you should make sure to remove the key event listener on unmount to prevent potential errors and memory leaks.
EDIT: If you are using hooks, you can use this useEffect
structure to produce a similar effect:
const ActionPanel = (props) => {
const escFunction = useCallback((event) => {
if (event.key === "Escape") {
//Do whatever when esc is pressed
}
}, []);
useEffect(() => {
document.addEventListener("keydown", escFunction, false);
return () => {
document.removeEventListener("keydown", escFunction, false);
};
}, []);
return (
<input />
)
};
EDIT for React 17: React changed the way that document-level event binding is handled, which may cause this implementation to stop working if at some point in the chain event.stopPropogation()
is called. You can ensure that this function is called first by changing the last argument of the listener to true
rather than false
. If you do this and also call event.stopPropogation()
, other handlers that used to be called will no longer take place, so I would suggest avoiding that call if at all possible.
Solution 2 - Javascript
You'll want to listen for escape's keyCode
(27) from the React SyntheticKeyBoardEvent
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} />
)
}
})
Brad Colthurst's CodePen posted in the question's comments is helpful for finding key codes for other keys.
Solution 3 - Javascript
Another way to accomplish this in a functional component, is to use useEffect
, like this:
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>);
}
Instead of console.log, you can use useState
to trigger something.
Solution 4 - Javascript
For a reusable React hook solution
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))
Solution 5 - Javascript
React uses SyntheticKeyboardEvent to wrap native browser event and this Synthetic event provides named key attribute,
which you can use like this:
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();
}
};
Solution 6 - Javascript
function handleEsc(event) {
if (event.keyCode === 27) {
close();
}
}
useEffect(() => {
window.addEventListener("keydown", handleEsc);
return () => {
window.removeEventListener("keydown", handleEsc);
};
}, []);
Solution 7 - Javascript
If you want to make this work directly in your component instead of a hook, or if like me you are using this in a hook that DOES NOT return a
useEffect(()=>{
document.addEventListener('keydown', (e) => {
e.key === 'Escape' && setOpenState(false)
})
return () => {
document.removeEventListener('keydown', (e) => e)
}
},[openState])
Solution 8 - Javascript
React Hook
useEffect(()=>{
document.addEventListener("keydown", keydownFunction, false);
return () => { document.removeEventListener("keydown", keydownFunction, false); };
}, []);
const keydownFunction =(event)=>{
if (event.key === "Escape") { setAdd(false); }
if (event.key === "+") { setAdd(true); }) }
}