Keyboard Shortcuts with React
In many web apps, you want to trigger an action in response to a keypress when a certain component is showing. I ran into this while playing with NuclearMail. Think gmail style "y to archive" kind of shortcuts. So, building with React, we might have a MessageView
component, which, when it is showing, we want to respond to the "y" keypress and archive the message. All we need is the keyboardjs
library and some decorator glue code:
// KeyBinder.js
var keyboardjs = require('keyboardjs');
module.exports = function decorateWithKeyBinding(
ComposedComponent,
) {
var displayName =
ComposedComponent.displayName ||
ComposedComponent.name ||
'Component';
class KeybinderEnhancer extends ComposedComponent {
bindKey(keyCombo: string, fn: () => void) {
this._keyBindings = this._keyBindings || [];
this._keyBindings.push(
keyboardjs.on(keyCombo, e => {
if (
['INPUT', 'TEXTAREA'].indexOf(
e.target.tagName,
) >= 0
) {
return;
}
fn();
}),
);
}
componentWillUnmount() {
if (super.componentWillUnmount) {
super.componentWillUnmount();
}
if (this._keyBindings) {
this._keyBindings.forEach(binding =>
binding.clear(),
);
}
}
}
KeybinderEnhancer.displayName = `Keybinder(${displayName})`;
return KeybinderEnhancer;
};
Notice how we ignore bindings fired when an input or textarea has focus is the target, so we don't trigger "archive" while you are searching for something with the letter "y".
Then, decorate your component with @KeyBinder
and call this.bindKey
in componentWillMount
:
@KeyBinder
class MessageView extends Component {
componentWillMount() {
this.bindKey('y', this._archive);
}
_archive = () => { ... };
}
Notice that _archive
uses class properties syntax to avoid using bind
. You could also make this into a mixin for easier use with createClass
style components, or just use the function call syntax instead, MessageView = KeyBinder(MessageView)
.
Let me know in the comments if you think this would be useful to publish to NPM!