I was recently tasked with creating an Easter Egg in our application to launch a troubleshooting menu. In the past, I've done this by clicking on a sequence of items or on what appears to be a disabled icon a few times. However, this time I decided to try a different approach and implement a React hook that listens for the Konami Code input on the keyboard. Thus, the useKonami
hook was born.
Here are the highlights:
- A hook that listens for a keyboard sequence (default is the Konami Code) on the window or a target element, and then calls an onUnlock callback after the sequence has been successfully entered by the user
- Zero external dependencies
- Built using standard React Hooks API's
- Optimized to avoid unnecessary rerenders
- Open source on GitHub
How does it work?
The hook uses React's useEffect
hook to register and unregister a keydown
event listener on the window or supplied target element. It then receives the keyboard down presses and compares them against the unlock sequence. Upon successfully pressing the sequence, the hook then calls the supplied onUnlock
callback. If the sequence is entered incorrectly, then the optional onReset
callback will be called. There is also an optional onKeyPress
callback that is invoked each time a key in the sequence is successfully pressed.
How is it optimized?
A good hook implementation shouldn't expect that the user is going to wrap their callbacks with React's useCallback
hook. Since the keyboard event listener isn't dependent on the supplied callback changes, the callbacks are stored in a mutable variable using React's useRef
hook. This avoids unnecessary re-renders and registering/unregistering of the keyboard event listener.
It should also be expected that a user may define their structured objects inline with the hook call as well. Therefore, the same approach was taken for a custom unlock sequence. Both of these optimizations would guard against the following example:
const MyUnlockableComponent = () => {
useKonamiCode({
// Callback defined inline
onUnlock: () => console.log('UNLOCKED'),
// Structured data defined in render function
sequence: ['x', 'y', 'z', 'z', 'y'],
});
return (<div>You can't unlock me!</div>);
}
Any other goodies?
- Fully typed codebase using TypeScript
- Fully documented API and repository
- Storybook examples available
- Continuous Integration with 100% Test Coverage
Thanks for reading my post and feel free to leave feedback here or in the GitHub repo!
Top comments (0)