Search code examples
javascriptreactjstypescriptreact-hooksrendering

My useEffect keeps re-rendering when I open new tabs or minimize the window


I am working on a portfolio website using React and I have diagonal moving circles animation showcasing different skills in each circle in the banner of the website. I am using the useEffect hook to create these circles and I have one immediately created when the website opens and then new ones are created every 5 seconds. However, the useEffect keeps re-rendering and many circles are created when I leave the website or minimize it or open another applicaiton. The website sometimes even crashes from the re-rendering.

Website link to see it: https://main--zingy-tiramisu-68c637.netlify.app/

Here is the component I am using for creating and displaying the skill circles:

import React, { useState, useEffect } from 'react';
import './banner-skills.component.css';
import SKILLS_DATA, { Skill } from '../../data/skills';

const BannerSkills = () => {
  const CREATE_CIRCLE_MS = 5000;
  const ARRAY_OF_SKILLS: Skill[] = [];

  useEffect(() => {
    const skillsContainer = document.getElementsByClassName('banner-skills-container')[0];
    let prevInitPos = Math.floor(Math.random() * 1650) - 150;

    const createDiagonalCircle = (): void => {
      const circle = document.createElement('div');
      circle.classList.add('skill-circle');
      skillsContainer.appendChild(circle);
      const logo = document.createElement('img');
      let randomSkill = SKILLS_DATA[Math.floor(Math.random() * SKILLS_DATA.length)];
      
      while(ARRAY_OF_SKILLS.indexOf(randomSkill) >= 0) {
        randomSkill = SKILLS_DATA[Math.floor(Math.random() * SKILLS_DATA.length)];
      }

      logo.src = randomSkill.img;
      ARRAY_OF_SKILLS.push(randomSkill);
      circle.appendChild(logo);

      let size = Math.floor(Math.random() * 70) + 70;
      circle.style.width = size + 'px';
      circle.style.height = size + 'px';

      let posTop = -150;
      let posLeft = Math.floor(Math.random() * (window.screen.width * 0.85)) - (window.screen.width * 0.08);

      while(Math.abs(posLeft - prevInitPos) < (window.screen.width * 0.13)) {
        posLeft = Math.floor(Math.random() * (window.screen.width * 0.85)) - (window.screen.width * 0.08);
      }
      
      prevInitPos = posLeft;
      let moveCirclesInterval = setInterval(frame, size - (size * 0.65));
      
      function frame() {
        if (posTop === 600) {
          skillsContainer.removeChild(circle);
          ARRAY_OF_SKILLS.splice(ARRAY_OF_SKILLS.indexOf(randomSkill), 1);
          clearInterval(moveCirclesInterval);
        } else {
          posTop++;
          posLeft++;
          circle.style.top = posTop + 'px';
          circle.style.left = posLeft + 'px';
          circle.style.visibility = 'visible';
        }
      }
    }

    createDiagonalCircle();

    const spawnCirclesInterval = setInterval(createDiagonalCircle, CREATE_CIRCLE_MS);

    return () => clearInterval(spawnCirclesInterval);
  }, [])

  return (
    <div className="banner-skills-container"></div>
  )
}

export default BannerSkills

I am creating the circles and animating them using JS to move diagonally. I call the function once at the start so a circle appears immediately when the website opens and then the function is called every 5 seconds. Any help is appreciated.

I have tried to create a boolean variable outside of the component as a condition to check if the useEffect is re-rendering or not but it did not work.


Solution

  • I have fixed this issue just now. All I needed to do is add a condition at the beginning of the createDiagonalCircle function in which if there are more than a certain number of circles present (used the array of skills for that) then it will return and stop the function. In my case, I put it to 7. as follows:

    const createDiagonalCircle = () => {
          if(ARRAY_OF_SKILLS.length >= 7) {
            return;
          }
          const circle = document.createElement('div');
          circle.classList.add('skill-circle');
          skillsContainer.appendChild(circle);
          const logo = document.createElement('img');
          let randomSkill = SKILLS_DATA[Math.floor(Math.random() * SKILLS_DATA.length)];
          
          while(ARRAY_OF_SKILLS.indexOf(randomSkill) >= 0) {
            randomSkill = SKILLS_DATA[Math.floor(Math.random() * SKILLS_DATA.length)];
          }
    
          logo.src = randomSkill.img;
          ARRAY_OF_SKILLS.push(randomSkill);
          circle.appendChild(logo);
    
          let size = Math.floor(Math.random() * 70) + 70;
          circle.style.width = size + 'px';
          circle.style.height = size + 'px';
    
          let posTop = -150;
          let posLeft = Math.floor(Math.random() * (window.screen.width * 0.85)) - (window.screen.width * 0.08);
    
          while(Math.abs(posLeft - prevInitPos) < (window.screen.width * 0.13)) {
            posLeft = Math.floor(Math.random() * (window.screen.width * 0.85)) - (window.screen.width * 0.08);
          }
          
          prevInitPos = posLeft;
          let moveCirclesInterval = setInterval(frame, size - (size * 0.65));
          
          function frame() {
            if (posTop === 600) {
              skillsContainer.removeChild(circle);
              ARRAY_OF_SKILLS.splice(ARRAY_OF_SKILLS.indexOf(randomSkill), 1);
              clearInterval(moveCirclesInterval);
            } else {
              posTop++;
              posLeft++;
              circle.style.top = posTop + 'px';
              circle.style.left = posLeft + 'px';
              circle.style.visibility = 'visible';
            }
          }
    }