-
-
Save gragland/b61b8f46114edbcf2a9e4bd5eb9f47f5 to your computer and use it in GitHub Desktop.
| import { useState, useEffect } from 'react'; | |
| // Usage | |
| function App() { | |
| // Call our hook for each key that we'd like to monitor | |
| const happyPress = useKeyPress('h'); | |
| const sadPress = useKeyPress('s'); | |
| const robotPress = useKeyPress('r'); | |
| const foxPress = useKeyPress('f'); | |
| return ( | |
| <div> | |
| <div>h, s, r, f</div> | |
| <div> | |
| {happyPress && '😊'} | |
| {sadPress && '😢'} | |
| {robotPress && '🤖'} | |
| {foxPress && '🦊'} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // Hook | |
| function useKeyPress(targetKey) { | |
| // State for keeping track of whether key is pressed | |
| const [keyPressed, setKeyPressed] = useState(false); | |
| // If pressed key is our target key then set to true | |
| function downHandler({ key }) { | |
| if (key === targetKey) { | |
| setKeyPressed(true); | |
| } | |
| } | |
| // If released key is our target key then set to false | |
| const upHandler = ({ key }) => { | |
| if (key === targetKey) { | |
| setKeyPressed(false); | |
| } | |
| }; | |
| // Add event listeners | |
| useEffect(() => { | |
| window.addEventListener('keydown', downHandler); | |
| window.addEventListener('keyup', upHandler); | |
| // Remove event listeners on cleanup | |
| return () => { | |
| window.removeEventListener('keydown', downHandler); | |
| window.removeEventListener('keyup', upHandler); | |
| }; | |
| }, []); // Empty array ensures that effect is only run on mount and unmount | |
| return keyPressed; | |
| } |
@felipe-dap
Yeah of course go ahead, it will be helpful for me if you do refactorings, I will get to learn more.
function testUseKeyPress() { const onPressSingle = () => { console.log('onPressSingle!') } const onPressMulti = () => { console.log('onPressMulti!') } useKeyPress('a', onPressSingle) useKeyPress('shift h', onPressMulti) }
onKeyPressed
Thanks @jeremytenjo
For folks attempting to capture
Metakey pressed in combination with other keys (Command+ke.g. on Macs), be warned that keyup events do not fire when theMetakey is still pressed. That means that this hook cannot be used reliably to detect when keys are unpressed. Read issue #3 here: https://web.archive.org/web/20160304022453/http://bitspushedaround.com/on-a-few-things-you-may-not-know-about-the-hellish-command-key-and-javascript-events/To work around this, I do not rely on keyup events at all but instead "unpress" automatically after a second. It's a bit hacky but serves my use case quite well (Command+k):
export function useKeyPress(targetKey: string) { // State for keeping track of whether key is pressed const [keyPressed, setKeyPressed] = useState<boolean>(false); // Add event listeners useEffect(() => { // If pressed key is our target key then set to true function downHandler({ key }: any) { if (!keyPressed && key === targetKey) { setKeyPressed(true); // rather than rely on keyup to unpress, use a timeout to workaround the fact that // keyup events are unreliable when the meta key is down. See Issue #3: // http://web.archive.org/web/20160304022453/http://bitspushedaround.com/on-a-few-things-you-may-not-know-about-the-hellish-command-key-and-javascript-events/ setTimeout(() => { setKeyPressed(false); }, 1000); } } window.addEventListener("keydown", downHandler); // Remove event listeners on cleanup return () => { window.removeEventListener("keydown", downHandler); }; }, []); // Empty array ensures that effect is only run on mount and unmount return keyPressed; }And if anyone is interested, here's my tiny
useKeyComboextension that can be used likeconst isComboPress = useKeyCombo("Meta+k");to captureCommand+k:export const useKeyCombo = (keyCombo: string) => { const keys = keyCombo.split("+"); const keyPresses = keys.map((key) => useKeyPress(key)); return keyPresses.every(keyPressed => keyPressed === true); };Lastly, I almost always just want to trigger some logic when these key conditions are met, so I made a wrapper hook that does that for me and allows for usage like this:
useOnKeyPressed("Meta+k", () => setIsQuickSearchOpen(true));:export const useOnKeyPressed = (keyCombo: string, onKeyPressed: () => void) => { const isKeyComboPressed = useKeyCombo(keyCombo); useEffect(() => { if (isKeyComboPressed) { onKeyPressed(); } }, [isKeyComboPressed]); };
have encountourred any problem while using this?
I would suggest adding 'blur' event to the window. I'm using this hook to see if 'Shift' is being pressed, but when I pressed 'Shift' and at same time go to another window (e.g devtools or another tab), the state wasn't being set as false, and when I went back to my tab the state was still true (since I release 'Shift' in another tab).
// ...
const setAsNotBeingPressed = useCallback(() => {
setKeyPressed(false);
}, []);
const setAsBeingPressed = useCallback(() => {
setKeyPressed(true);
}, []);
useEffet(() => {
// ....
window.addEventListener("blur", setAsBeingPressed);
return () => {
// ...
window.removeEventListener("blur", setAsNotBeingPressed);
}
})
@Suryakaran1234
Sure I can help you out on this. =)
Do you mind if I make a few refactorings along the way? Mostly for organization pourposes...
This is getting way off topic, so I made a repo in order for not cluttering this thread anymore.
Probably tomorrow there should be a demo on there. Find me there.
https://github.com/felipe-dap/useTraversalThroughInputs
and we can chat at
felipe-dap/useTraversalThroughInputs#1
Cheers.