skip to Main Content

I have this small project in Codesandbox using React

What I was trying to make this ship move around the screen using arrow keys

I have a function that detects when an key is pressed, then checks if this key is an arrow key, and if so, I change the coordinates of the image so the image can move around the screen

This is the code

import { useState } from "react";
import { constants } from "../../constants";
import "./space-ship.styles.css";

export const Spaceship = (): JSX.Element => {
  const [horizontalCords, setHorizontalCords] = useState<number>(0);
  const [verticalCords, setVerticalCords] = useState<number>(0);
  const validateArrowKey = (key: KeyboardEvent): void => {
    console.log("called one");
    if (key.keyCode === 37) {
      setHorizontalCords(horizontalCords - 10);
      return;
    }
    //left
    else if (key.keyCode === 39)
      return setHorizontalCords(horizontalCords + 10);
    //right
    else if (key.keyCode === 38) return setVerticalCords(verticalCords - 10);
    //up
    else if (key.keyCode === 40) return setVerticalCords(verticalCords + 10);
    // down;
  };

  window.addEventListener("keydown", validateArrowKey);

  return (
    <img
      style={{
        left: horizontalCords + "px",
        top: verticalCords + "px"
      }}
      className="space-ship"
      src={constants.space_ship}
      alt="Space ship"
    />
  );
};

I have a console log at the beggining of the function,and when I press the arrow key about 10 times this happens:
console log being called over 3000 times

And if I continue to press the keys the app crashes

Can anyone help me to stop the function being called a million times?

I tried to searching about re-renders in React and how state changes, I’ve tried using useCallbacks and changing the useEffect but nothing worked

2

Answers


  1. It is because window.addEventListener called every time when re-render (caused by setHorizontalCords), so the same listener keeping add more and more and never cleanup, you can use useEffect to handle the event of add/remove listener

    useEffect(() => {
        const validateArrowKey = (key: KeyboardEvent): void => {
            // ...
        };
    
        window.addEventListener("keydown", validateArrowKey);
    
        return () => window.removeEventListener("keydown", validateArrowKey);
    }, [])
    
    Login or Signup to reply.
  2. did you try cleaning up the event listener on the useEffect?

        useEffect(() => {
        window.addEventListener("keydown", validateArrowKey);
        
        return () => window.removeEventListener("keydown", validateArrowKey);
      }, [validateArrowKey]);
    

    I was able to reduce the rendering with this.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search