I am making a calculator with React and wanted to add the functionality for the user to input numbers directly from their keyboard. I used window.addEventListner()
with useEffect()
but when ever I press a key one the keyboard I get multiple inputs.
Lets say I pressed “1” once on my keyboard, then I will get “11111111” on the screen and printed to the console.
Here is the code I am using:
import { ACTIONS } from "./App";
import { useEffect } from "react";
export default function DigitButton({ dispatch, digit }) {
useEffect(() => {
window.addEventListener('keypress', (event) => {
dispatch({
type: ACTIONS.ADD_DIGIT,
payload: {
digit: event.key
}
})
});
});
return <button
onClick={() => {
dispatch({
type: ACTIONS.ADD_DIGIT,
payload: { digit }
})
}}
>
{digit}
</button>
}
You create/add an event listener each time DigitButton
renders, and never clean any of them up when the component unmounts. Add an empty dependency array to the useEffect
so the effect runs exactly once, and return a cleanup function to remove the event listener.
Example:
export default function DigitButton({ dispatch, digit }) {
useEffect(() => {
const handler = (event) => {
dispatch({
type: ACTIONS.ADD_DIGIT,
payload: { digit: event.key }
});
};
window.addEventListener('keypress', handler);
// return cleanup function to remove listener on unmount
return () => {
window.removeEventListener('keypress', handler);
};
}, []); // <-- empty dependency, run effect once
return (
<button
onClick={() => {
dispatch({
type: ACTIONS.ADD_DIGIT,
payload: { digit }
});
}}
>
{digit}
</button>
);
}
Your event is registering multiple times. It should only be called once, and you should also remove this event listener when this component unloads. To achieve this, start by passing an empty array ([]) as the second parameter of the useEffect function. Within this useEffect, return a method that removes the ‘keypress’ event listener.