0

I am new to react and have been trying to find an answer to this issue without much success. So, in this case, when I have all the function logic in the event listner's declaration it behaved weirdly ->

const [guess, setGuess] = useState<Array<String>>([]);
useEffect(() => {
    window.addEventListener("keydown", ({ key }) => {
        console.log(key);

        if (guess.length < 5) {
            const isChar = /^[a-zA-Z]$/.test(key);
            if (isChar) {
                setGuess((prev) => [...prev, key]);
            }
        }
    });
    return () => {
        window.removeEventListener("keydown", ({ key }) => {
            console.log(key);

            if (guess.length < 5) {
                const isChar = /^[a-zA-Z]$/.test(key);
                if (isChar) {
                    setGuess((prev) => [...prev, key]);
                }
            }
        });
    };
}, [guess.length]);

The way it works is it adds a lot of characters to the guess array instead of adding just the one whereas when I put all the function logic into a function and pass is as a callback like as shown below, it works as intended (Mainly, my goal was to have an event listener which should add the keypresses into an array, I am making a clone of Wordle)

useEffect(() => {
        function handleKeyDown({ key }: { key: string }) {
            console.log(key);

            if (guess.length < 5) {
                const isChar = /^[a-zA-Z]$/.test(key);
                if (isChar) {
                    setGuess((prev) => [...prev, key]);
                }
            }
        }

        window.addEventListener("keydown", handleKeyDown);
        return () => {
            window.removeEventListener("keydown", handleKeyDown);
        };
    }, [guess.length]);

Any help is greatly appreciated!

Amila Senadheera
  • 12,229
  • 15
  • 27
  • 43
  • 1
    Try with empty array as argument in the useEffect because multiple times event listeners getting added on change of guess length. – Javascript Hupp Technologies Feb 02 '22 at 05:48
  • I'm sorry, but that was my first thought and another question. If I remove the guess.length from the array and make it empty, then the whole if guess.length < 5 logic is never touched. Meaning the array overflows and go over 5 characters. The last code block is the way everything works as intended. I have no idea why adding guess.length there does the trick – Rudransh Sharma Feb 02 '22 at 05:52
  • May be this callback method will help you check here https://stackoverflow.com/a/55566585/7561290 – Javascript Hupp Technologies Feb 02 '22 at 05:56
  • I saw that answer before asking this question here, the problem is that, the answer does not tackle my problem and I was unable to understand how should I translate into my context and it does not explain the whole guess.lenght dependency issue! – Rudransh Sharma Feb 02 '22 at 06:01
  • You are adding new listeners every time when the array length gets changed. – Pankaj Chaturvedi Feb 02 '22 at 06:24
  • Yes, I understand that. But why does the last code block (the one with the function callback) works perfectly? Both of them are essentially the same code? – Rudransh Sharma Feb 02 '22 at 06:31

1 Answers1

0

You are adding new listeners on every state update. that is causing that function call multiple times. I have come up with a hack that you can use to remove listeners before adding new one.

 function handleKeyDown({ key }) {
    console.log(key, guess.length);
    if (guess.length < 5) {
      const isChar = /^[a-zA-Z]$/.test(key);
  if (isChar) {
    setGuess((prev) => [...prev, key]);
  }
}
}

useEffect(() => {
          //removing the older listener
      window.removeEventListener('keydown', handleKeyDown);
      //adding new listner
      window.addEventListener('keydown', handleKeyDown);
      return () => {
           window.removeEventListener('keydown', handleKeyDown);
      };
 }, [guess.length]);

it may help :)

  • Hi, Thank you for the answer. I understand removing the event listener and adding it again would help. However, it'll be amazing if you could answer why making the function handleKeyDown works and doing the same logic in the event handler's body does not work. And that why adding the guess.length dependency makes the array from overflowing (makes the whole if guess.length logic work) but if I am to remove that the array starts overflowing! – Rudransh Sharma Feb 02 '22 at 08:07
  • Actually what happening here is that the EventListner is remembering the function along with state (const value which was at firs place). that why it wasn't reflecting in related function. Now that overflow issue was there because with every state change you were creating new event handler which was also remembering last value. and every time you keypress a new function was evoked along with older ones but the state length was still the same that's why it was overflowing. – Pankaj Chaturvedi Feb 02 '22 at 09:49
  • just remove the removeEventlistner and see what it prints in guess.length(in handledown fun). you will get to know what i am talking abt – Pankaj Chaturvedi Feb 02 '22 at 09:51