Search code examples
javascriptcssreactjsperformanceappendchild

How to fix document append child slowing down react program?


I am attempting to make an incremental game similar to Cookie Clicker in React. I have added the click animation below for when the main button is clicked. This item is created as a div and appended as a child to the root of the button display area. The problem is that by doing so, it creates a delay between when the user is allowed to click again due to the slow processing time. I have removed the animation and the response time stays the same.

Is there any changes i could make to speed this process up?

GrowButton.scss

#growIcon {
  border-radius: 50%;
  width: 245px;
  height: 245px;
  box-shadow: 0 0 0 5px #263238;
  background-color: #263238;
  position: relative;
  display: flex;
}

#growRoot {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-width: 300px;
  min-height: 350px;
  height: 47.8vh;

  > [id^="x"] {
    width: 25px;
    height: 25px;
    position: absolute;
    color: white;
    font-weight: bold;
    animation: GoUp 1s forwards linear;
    -moz-animation: GoUp 1s forwards linear;
    -webkit-animation: GoUp 1s forwards linear;
    -o-animation: GoUp 1s forwards linear;
    -ms-animation: GoUp 1s forwards linear;
  }
}

@-moz-keyframes GoUp {
  0% {
    opacity: 1;
  }
  100% {
    top: 0px;
    opacity: 0;
  }
}
@keyframes GoUp {
  0% {
    opacity: 1;
  }
  100% {
    top: 0px;
    opacity: 0;
  }
}

@-webkit-keyframes GoUp {
  0% {
    opacity: 1;
  }
  100% {
    top: 0px;
    opacity: 0;
  }
}
@-o-keyframes GoUp {
  0% {
    opacity: 1;
  }
  100% {
    top: 0px;
    opacity: 0;
  }
}
@-ms-keyframes GoUp {
  0% {
    opacity: 1;
  }
  100% {
    top: 0px;
    opacity: 0;
  }
}

GrowButton.js

  buttonClick(event, props) {
    props.onclick();
    let growRoot = document.getElementById("growRoot");
    let newDiv = document.createElement("div");
    newDiv.innerText = x;
    newDiv.setAttribute("id", "x" + x++);
    newDiv.style.top = event.clientY + "px";
    newDiv.style.left = event.clientX - 10 + "px";
    growRoot.appendChild(newDiv);
  }

   <div id="growRoot">
       <img
            onClick={(e) => this.buttonClick(e, this.props)}
            id="growIcon"
            src="./Images/Plague_Icon_3.png"
            alt="Dummy growIcon"
        />
    < /div>

Solution

  • In your component state keep track of all elements

    elements is an array [] of html elements

    on button click push a new element to that array

    in render function you need to loop elements array and render each element

    I use functional component to demonstrate this, you should be able to adapt it to your needs.

    const test = () => {
    const [elements, setElements] = useState([<div> Some Element You Want Rendered </div>]);
    
    return (
    <div id="growRoot">
           <img
                onClick={(e) => {
                     const element = document.createElement("div");
                     element.innerText = x;
                     element.setAttribute("id", "x" + x++);
                     element.style.top = event.clientY + "px";
                     element.style.left = event.clientX - 10 + "px";
                     setElements([...elements].concat([element]))
                 }}
                id="growIcon"
                src="./Images/Plague_Icon_3.png"
                alt="Dummy growIcon"
            />
           {elements.map(element => element)} 
    </div>
    )
    }