Skip to content

useMagicKeys

Category
Export Size
1.08 kB
Last Changed
5 days ago

Reactive keys pressed state, with magical keys combination support

Demo

Usage

tsx
import { 
useMagicKeys
} from '@reause/core'
import {
useEffect
} from 'react'
const {
shift
,
space
,
a
/* keys you want to monitor */ } =
useMagicKeys
()
useEffect
(() => {
if (
space
)
console
.
log
('space has been pressed')
}, [
space
])
useEffect
(() => {
if (
shift
&&
a
)
console
.
log
('Shift + A have been pressed')
}, [
shift
,
a
])

NOTE

If you're using TypeScript with noUncheckedIndexedAccess enabled in your tsconfig.json, the destructured keys will have the type boolean | undefined.

The noUncheckedIndexedAccess TypeScript option adds undefined to any un-declared field accessed via index signatures. Since useMagicKeys() uses an index signature to allow accessing any key dynamically, TypeScript will treat destructured properties as potentially undefined for type safety.

A truthiness check narrows the value back to boolean:

tsx
const { 
shift
,
space
,
a
} =
useMagicKeys
()
if (
space
)
console
.
log
('space has been pressed')
if (
shift
&&
a
)
console
.
log
('Shift + A have been pressed')

Check the TypeScript documentation for more details about noUncheckedIndexedAccess.

Check out all the possible keycodes.

Combinations

You can magically use combinations (shortcuts/hotkeys) by connecting keys with + or _.

tsx
import { 
useMagicKeys
} from '@reause/core'
const
keys
=
useMagicKeys
()
const
shiftCtrlA
=
keys
['Shift+Ctrl+A']
useEffect
(() => {
if (
shiftCtrlA
)
console
.
log
('Shift + Ctrl + A have been pressed')
}, [
shiftCtrlA
])
tsx
import { 
useMagicKeys
} from '@reause/core'
const {
Ctrl_A_B
,
space
,
alt_s
/* ... */ } =
useMagicKeys
()
useEffect
(() => {
if (
Ctrl_A_B
)
console
.
log
('Control+A+B have been pressed')
}, [
Ctrl_A_B
])

You can also use useWhenever function to make it shorter

tsx
import { 
useMagicKeys
} from '@reause/core'
import {
useWhenever
} from '@reause/shared'
const
keys
=
useMagicKeys
()
useWhenever
(
keys
.
shift_space
, () => {
console
.
log
('Shift+Space have been pressed')
})

Current Pressed keys

A special property current is provided to representing all the keys been pressed currently.

tsx
import { 
useMagicKeys
} from '@reause/core'
const {
current
} =
useMagicKeys
()
console
.
log
(
current
) // Set { 'control', 'a' }
useWhenever
(
current
.
has
('a') && !
current
.
has
('b'),
() =>
console
.
log
('A is pressed but not B'),
)

Key Aliasing

tsx
import { 
useMagicKeys
} from '@reause/core'
const {
shift_cool
} =
useMagicKeys
({
aliasMap
: {
cool
: 'space',
}, })
useEffect
(() => {
if (
shift_cool
)
console
.
log
('Shift + Space have been pressed')
}, [
shift_cool
])

By default, we have some preconfigured alias for common practices.

Conditionally Disable

You might have some <input /> elements in your apps, and you don't want to trigger the magic keys handling when users focused on those inputs. There is an example of using useActiveElement to do that.

tsx
import { 
useActiveElement
,
useMagicKeys
} from '@reause/core'
import {
useEffect
} from 'react'
const
activeElement
=
useActiveElement
()
const
notUsingInput
=
activeElement
?.
tagName
!== 'INPUT'
&&
activeElement
?.
tagName
!== 'TEXTAREA'
const {
tab
} =
useMagicKeys
()
useEffect
(() => {
if (
tab
&&
notUsingInput
)
console
.
log
('Tab has been pressed outside of inputs!')
}, [
tab
,
notUsingInput
])

Custom Event Handler

tsx
import { 
useMagicKeys
} from '@reause/core'
const {
ctrl_s
} =
useMagicKeys
({
passive
: false,
onEventFired
(
e
) {
if (
e
.
ctrlKey
&&
e
.
key
=== 's' &&
e
.
type
=== 'keydown')
e
.
preventDefault
()
}, })

⚠️ This usage is NOT recommended, please use with caution.

Reactive Mode

React state is always "reactive" — the reactive: true option is accepted for API compatibility but has no effect, values are plain booleans either way.

tsx
import { 
useMagicKeys
} from '@reause/core'
const
keys
=
useMagicKeys
({
reactive
: true })

Type Declarations

ts
export interface UseMagicKeysOptions<Reactive extends boolean> {
    reactive?: Reactive;
    target?: RefObject<EventTarget | null | undefined>;
    aliasMap?: Record<string, string>;
    passive?: boolean;
    onEventFired?: (e: KeyboardEvent) => void | boolean;
}

export interface MagicKeysInternal {
    current: ReadonlySet<string>;
}

export type UseMagicKeysReturn<Reactive extends boolean> = Readonly<Record<string, Reactive extends true ? boolean : boolean> & MagicKeysInternal>

export const DefaultMagicKeysAliasMap: Readonly<Record<string, string>>

export function useMagicKeys<T extends boolean = false>(options?: UseMagicKeysOptions<T>): UseMagicKeysReturn<T>

Source

Source · Demo · VueUse

Contributors

hairyf

Changelog

v0.1.0 on
2795d - fix(core): resolve useMagicKeys audit findings (#597)
8fddc - chore!: remove all Vue-only Maybe* types and getter unions, adopt React Ref (#462)

Released under the MIT License. v0.1.8