This is a custom hook based on React keyboard events, integrating event queue management solutions
npm install react-khooksWelcome to the project documentation. You can click the link below to switch to the Chinese version.
> 📖 中文 | English
``bash`
npm i react-khooks --save
`jsx`
// Import what you need
import { useKeyEvent } from 'react-khooks'; // Keyboard hook
import { emitter } from 'react-khooks'; // Global event queue instance
A custom React hook for keyboard events, mainly used in internal projects. It supports multiple listeners for the same key combination, but only the most recently added listener will be triggered. You can use this mechanism to implement a queue system for your business logic. Alternatively, you can use emitter to handle event priority.
Note: Written in beginner-level TypeScript. PRs and issues are welcome!
| Parameter | Description |
| --- | --- |
| keyName | Keyboard key name or keyCode. Required. For combo keys, use +to connect (e.g.,ctrl+z). Be mindful of system/browser conflicts. |callback
| | Callback function. Receives KeyboardEventas default parameter. Required. |toolEventName
| | Custom event name, serves as the unique identifier in the queue. Required. |type
| | Trigger on keyupor keydown. Default:keyup. |delayTime
| | Throttle/debounce delay in ms.0means disabled. Recommended for keydown. |delayType
| | 1for throttle,2for debounce. Default:1. |
Supports combinations like ctrl+alt+shift+key.
You can use the passed-in event object for more logic.
useKeyEvent returns the global emitter, which can also be imported directly for advanced use.
> ✅ Basic Example
`jsx
import React from 'react';
import { useKeyEvent } from 'react-khooks';
export default () => {
const handleClick = () => {
alert('Pressed Z');
};
useKeyEvent({ keyName: 'z', callback: handleClick, toolEventName: 'alert' });
return
> 💡 Modifier Keys (ctrl/alt/shift + key)
`jsx
import React, { useState } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const handleClick = () => setNum(num + 1);
useKeyEvent({ keyName: 'alt+v', callback: handleClick, toolEventName: 'inc_alt' });
useKeyEvent({ keyName: 'ctrl+v', callback: handleClick, toolEventName: 'inc_ctrl' });
useKeyEvent({ keyName: 'shift+v', callback: handleClick, toolEventName: 'inc_shift' });
return (
Press ctrl/alt/shift + v to increase count
Count: {num}
);
};
`> 🔁 Dynamic Hotkey Change
`jsx
import React, { useState } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const [hotKey, setHotKey] = useState('m');
const handleClick = () => setNum(num + 1);
useKeyEvent({ keyName: hotKey, callback: handleClick, toolEventName: 'dynamic_key' });
return (
Press {hotKey} to increase count
Count: {num}
);
};
`> 🌀 Throttle / Debounce
`jsx
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const handleClick = useCallback(() => setNum(num + 1), [num]);
useKeyEvent({
keyName: 'q',
callback: handleClick,
toolEventName: 'throttle_q',
delayTime: 500,
type: 'keydown',
delayType: 1,
});
useKeyEvent({
keyName: 'w',
callback: handleClick,
toolEventName: 'debounce_w',
delayTime: 500,
type: 'keydown',
delayType: 2,
});
return (
Throttle: Hold "q" (fires every 500ms)
Debounce: Hold "w" (fires after 500ms release)
Count: {num}
);
};
`Recommended Usage of
useCallback with useKeyEvent> ⚠️ It is strongly recommended to wrap your callback functions with
useCallback to avoid frequent subscription/unsubscription when the component re-renders.$3
`jsx
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const handleClick = useCallback(() => {
setNum((num) => num + 1);
}, []);
useKeyEvent({ keyName: 'a', callback: handleClick, toolEventName: 'add' });
return (
Press 'a' to update the num state defined via useState
num: {num}
);
};
`$3
`jsx
import React, { useState, useCallback } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const handleClick = useCallback(() => {
setNum(num - 1);
}, [num]);
useKeyEvent({ keyName: 's', callback: handleClick, toolEventName: 'reduce' });
return (
Press 's' to update the num state defined via useState
num: {num}
);
};
`⚠️ Not Recommended: Using inline or re-created callbacks This approach was previously discouraged due to performance concerns—frequent unsubscribe/subscribe on every render.
✅ Now optimized: Re-renders no longer cause frequent subscribe/unsubscribe. 🔁 However, it's still recommended to use the previous two approaches for maintaining up-to-date state.
`jsx
import React, { useState } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const handleClick = (param) => {
setNum(num + param);
};
useKeyEvent({ keyName: 'ctrl+x', callback: () => handleClick(1), toolEventName: 'add_2' });
useKeyEvent({ keyName: 'shift+x', callback: () => handleClick(-1), toolEventName: 'reduce_2' });
return (
Press ctrl+x to increase num
Press shift+x to decrease num
num: {num}
);
};
`> ❄️ Freeze / Unfreeze All Events
`jsx
import React, { useState } from 'react';
import { useKeyEvent } from 'react-khooks';export default () => {
const [num, setNum] = useState(0);
const handleClick = () => setNum((prev) => prev + 1);
const { emitter } = useKeyEvent({ keyName: 'f', callback: handleClick, toolEventName: 'add' });
return (
Press "f" to increase count
emitter.freezeAll()}>Freeze
emitter.unfreezeAll()}>Unfreeze
Count: {num}
);
};
``
