Having fun with React Hooks
Tags: react, typescript
Published: 2021-03-23 03:51:19


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](https://en.wikipedia.org/wiki/Konami_Code) input on the keyboard. Thus, the [`useKonami`](https://www.npmjs.com/package/use-konami) 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](https://github.com/chris-feist/use-konami)

### How does it work?
The hook uses React's [`useEffect`](https://reactjs.org/docs/hooks-reference.html#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`](https://reactjs.org/docs/hooks-reference.html#usecallback) hook. Since the keyboard event listener isn't dependent on the supplied callback changes, [the callbacks are stored](https://github.com/chris-feist/use-konami/blob/4161ad60adab9c6e757500d1960a661c4b77ade3/src/index.ts#L70) in a mutable variable using React's [`useRef`](https://reactjs.org/docs/hooks-reference.html#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](https://github.com/chris-feist/use-konami/blob/4161ad60adab9c6e757500d1960a661c4b77ade3/src/index.ts#L68). Both of these optimizations would guard against the following example:

```javascript
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](https://github.com/chris-feist/use-konami/blob/main/README.md) API and repository
- [Storybook examples available](https://chris-feist.github.io/use-konami/)
- [Continuous Integration](https://travis-ci.com/chris-feist/use-konami) with [100% Test Coverage](https://app.codecov.io/gh/chris-feist/use-konami)

---

Thanks for reading my post and feel free to leave feedback here or in the [GitHub repo](https://github.com/chris-feist/use-konami)!