Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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;
}
@awnigharbia

This comment has been minimized.

Copy link

commented Nov 16, 2018

import React, { useState, useEffect } from "react";

export default function useKeyPress(targetKey) {
  // State for keeping track of whether key is pressed
  const [keyPressed, setKeyPressed] = useState(false);
  let prevKey = "";

  // If pressed key is our target key then set to true
  function downHandler({ key }) {
    // checking keep pressing re-rendering
    if (prevKey === targetKey) return;

    if (key === targetKey) {
      setKeyPressed(true);
      prevKey = key;
    }
  }

  // If released key is our target key then set to false
  const upHandler = ({ key }) => {
    if (key === targetKey) {
      setKeyPressed(false);
      prevKey = "";
    }
  };
  // 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;
}

I suggest checking the long pressing for the key itself so it prevents it from re-reding.

@tikotzky

This comment has been minimized.

Copy link

commented Feb 13, 2019

To make the hook more robust it should be updated to rerun the effect if the targetKey changes.

here is an updated gist

and here is a codesandbox example

@metamn

This comment has been minimized.

Copy link

commented May 26, 2019

Prettier arbitrarily modifies the code on save by replacing [] with [downHandler, upHandler]:

// 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);
      };
    },
    [downHandler, upHandler]
  ); // Empty array ensures that effect is only run on mount and unmount

This results in a warning in the console:

./src/hooks/useKeyPress.js
  Line 8:   The 'downHandler' function makes the dependencies of useEffect Hook (at line 33) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'downHandler' definition into its own useCallback() Hook  react-hooks/exhaustive-deps
  Line 15:  The 'upHandler' function makes the dependencies of useEffect Hook (at line 33) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'upHandler' definition into its own useCallback() Hook      react-hooks/exhaustive-deps

This can be solved by moving the two functions inside useEffect:

// Add event listeners
  useEffect(
    () => {
      // 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);
        }
      };
      window.addEventListener("keydown", downHandler);
      window.addEventListener("keyup", upHandler);

      // Remove event listeners on cleanup
      return () => {
        window.removeEventListener("keydown", downHandler);
        window.removeEventListener("keyup", upHandler);
      };
    },
    [targetKey]
  ); 

Now the console becomes clear.

@metamn

This comment has been minimized.

Copy link

commented May 26, 2019

Please note the key codes are following the event.key notation instead of the older but widely used event.keyCode.

Thus Enter will become Enter instead of 13, or the right arrow ArrowRight instead of 39

Source: https://stackoverflow.com/questions/27827234/how-to-handle-the-onkeypress-event-in-reactjs
(Old) Key codes: https://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes

@e2o

This comment has been minimized.

Copy link

commented Jun 6, 2019

I adapted a version of this hook to use in my own app.
I added optional event handlers that you could use in your component to fire onPressDown & onPressUp:

import { useEffect, useState } from 'react';

export function useKeyPress(targetKey, onPressDown = () => {}, onPressUp = () => {}) {
	// State for keeping track of whether key is pressed
	const [keyPressed, setKeyPressed] = useState(false);

	useEffect(() => {
		// If pressed key is our target key then set to true
		function downHandler({ key }) {
			if (key === targetKey) {
				setKeyPressed(true);
				onPressDown();
			}
		}

		// If released key is our target key then set to false
		const upHandler = ({ key }) => {
			if (key === targetKey) {
				setKeyPressed(false);
				onPressUp();
			}
		};

		// Add event listeners
		window.addEventListener('keydown', downHandler);
		window.addEventListener('keyup', upHandler);

		// Remove event listeners on cleanup
		return () => {
			window.removeEventListener('keydown', downHandler);
			window.removeEventListener('keyup', upHandler);
		};
	});

	return keyPressed;
}

Usage:

const  escapePressed = useKeyPress('Escape', onPressDown, onPressUp);
@sethdavis512

This comment has been minimized.

Copy link

commented Jun 24, 2019

I like that implementation @e2o - thanks for sharing all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.