react-hotkeys v2.0.0 Release Notes
Release Date: 2019-07-12 // over 5 years ago-
๐ This release represents a complete re-write of the internals and much of the interface of
react-hotkeys
. Mousetrap has been removed as a dependency, and insteadreact-hotkeys
works on the top of the ReactSyntheticEvent
events system.โฌ๏ธ > Upgrading from v1.*?
๐ > This release is the same as the
v2.0.0-pre9
and these release notes contain a summary of the changes moving from v1.* tov2.0.0
.โฌ๏ธ > Upgrading from v2.0.0-pre*?
๐ > Ignore this set of release notes, and follow the notes for each each pre-release starting from the version you are using, up to
v2.0.0-pre9
.๐ฅ Breaking Changes
__mousetrap__ is no longer available
โก๏ธ Rather unsurprisingly, with the removal of Mousetrap, it's no longer possible to access the private Mousetrap instance to configure it. Many of the use-cases for needing to do so have been covered by API updates described below. If your use-case is not covered, however, please create a new issue.
๐ HotKeyMapMixin has been removed
This was an internal part of
react-hotkeys
and is no longer used, and so it does not makes sense to continue to provide it.๐ FocusTrap has been removed
Similar to
HotKeyMapMixin
, this was an internal part ofreact-hotkeys
and is no longer used, and so it does not makes sense to continue to provide it.withHotKeys has been replaced
๐ The old
withHotKeys
function has been repurposed and should not be used in the same way the old one was (more on this later). The old implementation is still available asdeprecatedWithHotKeys
, but it is no longer officially supported and will be removed in future versions.Before:
import {withHotKeys} from 'react-hotkeys';const ACTION\_KEY\_MAP = { 'changeColor': 'alt+c', };withHotKeys(ACTION\_KEY\_MAP)(HOCWrappedNode);
After (suggested):
You can write your own function or HoC that supplies the same keyMap to a component passed to it.
import {HotKeys} from 'react-hotkeys';const ACTION\_KEY\_MAP = { 'changeColor': 'alt+c', };function withActions(keyMap, Component) { return function(props) { return( \<HotKeys keyMap={keyMap}\>\<Component {...props} /\>\</HotKeys\> ); } }deprecatedWithHotKeys(ACTION\_KEY\_MAP)(HOCWrappedNode);
โช After (to restore old behaviour):
import {deprecatedWithHotKeys} from 'react-hotkeys';const ACTION\_KEY\_MAP = { 'changeColor': 'alt+c', };deprecatedWithHotKeys(ACTION\_KEY\_MAP)(HOCWrappedNode);
๐ focused and attach props hav been removed
The
focused
andattach
props were introduced as a way to bind keymaps that were either always active, or available outside of the React application. They are no longer needed with the introduction ofGlobalHotkeys
.Before:
import {HotKeys} from 'react-hotkeys';\<HotKeys focused={true} attach={document} keyMap={keyMap} handlers={handlers}\>\<div\> My content \</div\>\</HotKeys\>
After:
import {GlobalHotKeys} from 'react-hotkeys';\<GlobalHotKeys keyMap={keyMap} handlers={handlers} /\>\<div\> My content\</div\>
๐ Hard sequences are deprecated and turned off by default
๐ Hard sequences (handlers associated to actions with names that are valid key sequence strings that implicitly define actions that are matched by the corresponding key sequence) are now deprecated and turned off by default. They can be re-enabled (at a performance penalty) using the
enableHardSequences
configuration option:import {configure} from 'react-hotkeys';configure({ enableHardSequences: true});
0๏ธโฃ Default key event is now always keydown
0๏ธโฃ
react-hotkeys
used to rely on the implicit behaviour of Mousetrap to guess the best key event based on the keys involved (this was, roughly speaking,keydown
for non-modifier keys andkeypress
for modifier keys). Now by default,react-hotkeys
will match hotkey sequences on thekeydown
event (or, more precisely: on thekeydown
event of the last key to complete the last combination in a sequence).If you want to trigger a single action on a different key event, you can use the object syntax and the
action
attribute to explicitly set which key event you wish to bind to:const keyMap = { CONTRACT: 'alt+down', COMMAND\_DOWN: {sequence: 'command', action: 'keydown'}, };
๐ง If you want to change the default key event for all hotkeys, you can use the
defaultKeyEvent
option of the configuration API.Keypress events are now simulated for modifier keys
๐ป Before, you had to rely on Mousetrap guessing the best key event for your key combination (unless you explicitly defined an event) and if you bound to a key combination to a keypress event, it did not work (the browser does not emit these events for modifier keys).
react-hotkeys
now simulates these events internally (they aren't emitted to the rest of your React components) so you do not have to worry about if your key combination includes a modifier key when binding to keypress.This shouldn't affect most library consumers, but is listed as a breaking change as it can conceivably cause different behaviour for the same application code.
stopPropagation() is now observed
Because
react-hotkeys
now uses the React event system,event.stopPropagation()
now works as expected. If you have event listeners in your React application that callevent.stopPropagation()
before the event reaches a<HotKeys />
or<GlobalHotkeys />
component that has akeyMap
prop,react-hotkeys
never sees it.If you want to hide that a key has been pressed at all, you will need to capture all 3 of the
keydown
,keypress
andkeyup
events.0๏ธโฃ Key events from input, select and textarea tags are ignored by default
If you were ignoring key events from certain inputs by overriding the
stopCallback
function on mousetrap as has been previously suggested, you no longer need to.0๏ธโฃ By default, all key events that originate from
<input>
,<select>
or<textarea>
, or have aisContentEditable
attribute oftrue
are ignored byreact-hotkeys
.๐ง If this is not what you want for your application, you can modify the list of tags using the
ignoreTags
configuration option or if you need additional control, you can specify a brand new function using theignoreEventsCondition
configuration option.Before:
const mousetrap = this.hotKeys.\_\_mousetrap\_\_;mousetrap.\_\_proto\_\_.stopCallback = (e, element) =\> { // Your custom logic here};
After:
๐ง (After you've confirmed the default function does not match your use-case):
import {configure} from 'react-hotkeys';configure({ ignoreEventsCondition: function(event) { const { target: element } = event; // Your custom logic here } });
stopPropagation() is called on events that are ignored
0๏ธโฃ By default,
react-hotkeys
callsstopPropagation()
on keyboard events that it ignores, at the first<HotKeys>
component with a non-emptykeyMap
prop (or a hard sequence defined in thehandlers
prop, if hard sequences are enabled). This makesreact-hotkeys
more efficient, but may be hiding keyboard events from other key listeners you have in place in your React app.๐ง You can disable this behaviour using the
stopEventPropagationAfterIgnoring
configuration option:import {configure} from 'react-hotkeys';configure({ stopEventPropagationAfterIgnoring: false});
โก๏ธ Updates to keyMaps and handlers after focus are ignored by default
๐ For performance reasons, by default
react-hotkeys
takes thekeyMap
andhandlers
prop values when<HotKeys>
components are focused and when<GlobalHotKeys>
components are mounted. It ignores all subsequent updates to their values when these props change.If you need the ability to change them while a
<HotKeys>
component is still in focus, or while<GlobalHotKeys>
is still mounted, then you can pass theallowChanges
prop, permitting this behaviour for the particular component.import {HotKeys} from 'react-hotkeys';class MyComponent extends React.Component { render() { return ( \<HotKeys keyMap={keyMapThatChanges} handler={handlersThatChange} allowChanges\>\</HotKeys\> ); } }
๐ If you need to do this for all your
<HotKeys>
and<GlobalHotKeys>
components, you can use theignoreKeymapAndHandlerChangesByDefault
option for the Configuration API. This should normally never be done, as it can have significant performance implications.import {configure} from 'react-hotkeys';configure({ ignoreKeymapAndHandlerChangesByDefault: false});
๐ New features
๐ป Browser keynames are now supported in key sequence definitions
๐ป You can now use browser key names when defining your sequences, in addition to Mousetrap's name for the keys.
๐ New withHotKeys HoC - a way to avoid rendering a wrapping div
If wrapping your component in a DOM-mountable node is not acceptable, or you need more control over how the
react-hotkeys
props are applied, then the newwithHotKeys()
HoC is available.Simple use-case
The simplest use-case of
withHotKeys()
is to simply pass it your component class as the first argument. What is returned is a new component that will accept all of the same props as a<HotKey>
component, so you can specify key maps and handlers at render time, for example.The component you wrap must take responsibility for passing the
hotKeys
props to a DOM-mountable element. If you fail to do this, key events will not be detected when a descendant of the component is in focus.import {withHotKeys} from 'react-hotkeys';class MyComponent extends Component { render() { /\*\* \* Must unwrap hotKeys prop and pass its values to a DOM-mountable \* element (like the div below).\*/const {hotKeys, ...remainingProps} = this.props; return ( \<div { ... { ...hotKeys, ...remainingProps } } \>\<span\>My HotKeys are effective here\</span\> { this.props.children } \</div\> ) } }const MyHotKeysComponent = withHotKeys(MyComponent);const keyMap = { TEST: 't'};const handlers = { TEST: ()=\> console.log('Test') };\<MyHotKeysComponent keyMap={ keyMap } handlers={ handlers }\>\<div\> You can press 't' to log to the console. \</div\>\</MyHotKeysComponent\>
0๏ธโฃ Pre-defining default prop values
0๏ธโฃ You can use the second argument of
withHotKeys
to specify default values for any props you would normally pass to<HotKeys />
. This means you do not have to specify them at render-time.๐ > If you do provide prop values when you render the component, these will be merged with (and override) those defined in the second argument of
withHotKeys
.import {withHotKeys} from 'react-hotkeys';class MyComponent extends Component { render() { /\*\* \* Must unwrap hotKeys prop and pass its values to a DOM-mountable \* element (like the div below).\*/const {hotKeys, ...remainingProps} = this.props; return ( \<div { ... { ...hotKeys, ...remainingProps } } \>\<span\>My HotKeys are effective here\</span\> { this.props.children } \</div\> ) } }const keyMap = { TEST: 't'};const handlers = { TEST: ()=\> console.log('Test') };const MyHotKeysComponent = withHotKeys(MyComponent, {keyMap, handlers});/\*\* \* Render without having to specify prop values \*/\<MyHotKeysComponent\>\<div\> You can press 't' to log to the console. \</div\>\</MyHotKeysComponent\>
๐ New GlobalHotKeys component
<GlobalHotKeys>
components match key events that occur anywhere in the document (even when no part of your React application is in focus). They replace the need for thefocused
andattach
prop.const keyMap = { SHOW\_ALL\_HOTKEYS: 'shift+?' };const handlers = { SHOW\_ALL\_HOTKEYS: this.showHotKeysDialog };\<GlobalHotKeys keyMap={ keyMap } handlers={ handlers } /\>
๐ป
<GlobalHotKeys>
generally have no need for children, so should use a self-closing tag (as shown above). The only exception is when you are nesting other<GlobalHotKeys>
components somewhere in the descendants (these are mounted before their parents, and so are generally matched first).๐ New IgnoreKeys component
If you want
react-hotkeys
to ignore key events coming from a particular area of your app when it is in focus, you can use the new<IgnoreKeys/>
component:import {IgnoreKeys} from 'react-hotkeys';\<IgnoreKeys\>/\*\* \* Children that, when in focus, should have its key events ignored by \* react hotkeys\*/\</IgnoreKeys\>
๐ New ObserveKeys component
0๏ธโฃ The
<ObserveKeys />
component (and correspondingwithObserveKeys
method to avoid wrapper div) are for defining white-list exceptions to the the defaultignoreEventsCondition
<ObserveKeys only={'Escape'}> <input autoFocus onChange={({target: {value}}) => this.setState({ filter: value })} value={filter} placeholder='Filter' /> </ObserveKeys>
How actions and handlers are resolved
Regardless of where
<GlobalHotKeys>
components appear in the render tree, they are matched with key events after the event has finished propagating through the React app (if the event originated in the React at all). This means if your React app is in focus and it handles a key event, it will be ignored by the<GlobalHotKeys>
components.๐ The order used for resolving actions and handlers amongst
<GlobalHotKeys>
components, is the order in which they mounted (those mounted first, are given the chance to handle an action first). When a<GlobalHotKeys>
component is unmounted, it is removed from consideration. This can get less deterministic over the course of a long session using a React app as components mount and unmount, so it is best to define actions and handlers that are globally unique.๐ It is recommended to use
<HotKeys>
components whenever possible for better performance and reliability.You can use the autofocus attributes or programmatically manage focus to automatically focus your React app so the user doesn't have to select it in order for hot keys to take effect. It is common practice to place a
<HotKeys>
component towards the top of your application to match hot keys across your entire React application.Can now generate an application key map
It's now possible to generate a list of hotkeys for the application to display to the user.
๐ New innerRef prop
Thanks to #124,
<ReactHotkeys />
now accepts aninnerRef
prop:class MyComponent extends Component { componentDidMount() { this.\_container.focus(); } render() { return ( \<HotKeys innerRef={ (c) =\> this.\_container = c } \> My focusable content \</div\> ) } }
Key map name, description and groups
You can now specify name, description and groups for key maps #154 (More info)
SHOW\_DIALOG: { name: 'Display keyboard shortcuts', sequence: 'shift+?', action: 'keyup' }
Custom key codes
You can now define custom key codes for WebOS and other environments (#156) (More info)
import {configure} from 'react-hotkeys';configure({ customKeyCodes: { 10009: 'BackTV' } })
Dynamic key maps at runtime
You can now set dynamic key maps at runtime (#158) (More info)
Small changes
- โ Removed upper limit on React peer dependency version.
cmd
is now an accepted alias forCommand
/Meta
key
๐ New Configuration configuration API for global settings
๐ง The default behaviour across all
<HotKeys>
and<GlobalHotkeys>
components is configured using theconfigure
method.๐ง > configure() should be called as your app is initialising and before the first time you mount a
<HotKeys>
component anywhere in your app.๐ New comprehensive logging
๐ฒ
react-hotkeys
now provides comprehensive logging of all of its internal behaviour and allows setting one of 6 log levels.๐ The default level is
warn
, which provides warnings and errors only, and is generally sufficient for most usage. However, if you are troubleshooting an issue or reporting a bug, you should increase the log level todebug
orverbose
to see what is going on, and be able to communicate it concisely.Optimisations
๐ Some improved and additional optimisations have been implemented as part of this release.
๐ Bugfixes
Previous changes from v2.0.0-pre9
-
Potentially Breaking Changes
- ๐ง The configuration option
allowCombinationSubmatches
is now ignored when theCmd
key is pressed down (submatches are always allowed whenCmd
is pressed). This is to fix the following bug.
๐ Bugfixes
- ๐ Fix moving between actions bound to the
Cmd
(Meta
) key (e.g.cmd+1
->cmd+2
without releasingCmd
) #201
- ๐ง The configuration option