logic in react-transition-group, rendering elements in the DOM first and then applying the CSS stylings with CSSTransition

20 views Asked by At

I am using the react transition group library to apply a slide down fade animation to the navbar 1 second after the page loads, the main issue falls on elements that are nested in a .map function while the rest render just fine. This is how my navComponent looks like (with comments):

const Navbar = () => {

  //This code right here handles the visibility of the navbar on scrolldown/scrollup
  const [prevScrollPos, setPrevScrollPos] = useState(0);
  const [visible, setVisible] = useState(true);

  const handleScroll = () => {
    const currentScrollPos = window.scrollY;
    setVisible(prevScrollPos > currentScrollPos || currentScrollPos < 10);
    setPrevScrollPos(currentScrollPos);
  };

  useEffect(() => {
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [prevScrollPos, visible]);
  
  //Fade animations ***HERE IS THE ISSUE (I think)***//
  //Hold state in order to mount the element after timer

  const [isMounted, setIsMounted] = useState(false);

  //Creates a timer to set "isMounted" after 1000ms

  useEffect(() => {
    const timeout = setTimeout(() => {setIsMounted(true)}, 1000);
    return () => clearTimeout(timeout)
  }, [])

  //Elements and their Refs to be used with CSSTransitions
  const logoRef = useRef()
  const logo = (
    <div className='logo' ref={logoRef} style={{ transitionDelay: `100ms` }}>
        <p>RAUNCHO</p>
      </div>
  ); 
  //All li elements have these <Link> attributes, however for code readability purposes I will only leave them for 'li1', the rest will be deleted
  const li1Ref = useRef();
  const li1 = (
    <li className="navbar-item">
      <Link
        className='navLink-item'
        activeClass="active"
        to="about"
        spy={true}
        smooth={true}
        offset={0}
        duration={500}>
          <span id='codeFont'>01.</span>
          About
      </Link>
    </li>
  )
  const li2Ref = useRef();
  const li2 = (
    <li className="navbar-item">
      <Link>
        <span id='codeFont'>02.</span>
        Experience
      </Link>
    </li>
  )
  const li3Ref = useRef();
  const li3 = (
    <li className="navbar-item">
      <Link>
        <span id='codeFont'>03.</span>
        Projects
      </Link>
    </li>
  )
  const li4Ref = useRef();
  const li4 = (
    <li className="navbar-item">
      <Link>
        <span id='codeFont'>04.</span>
        Contact
      </Link>
    </li>
  )
  const li5Ref = useRef();
  const li5 = (
    <li className="navbar-item">
      <button className='CTA-button' id="resume">
        Resume
      </button>
    </li>
  )
  
  //Element array to iterate through
  const navElements = [[li1,li1Ref],[li2,li2Ref],[li3,li3Ref],[li4,li4Ref],[li5,li5Ref]]

  return (
    <nav className={`navbar ${visible ? 'visible' : 'hidden'} ${prevScrollPos <= 10 ? 'at-top' : ''}`}>
      <TransitionGroup component={null}>
        {isMounted && (
          <>
          //Logo element renders just fine

          <CSSTransition in={isMounted} nodeRef={logoRef} classNames="fade" timeout={1000}>
            {logo}
          </CSSTransition>
          
          //As mentioned above, li elements are rendered first at once in the DOM, then the animations are applied

          <ul className="navbar-list">
            {navElements.map(([navLi,ref], i) => {
              return (
                <CSSTransition key={i} in={isMounted} classNames="fadedown" timeout={1000} nodeRef={ref}>
                  <span style={{ transitionDelay: `${i + 1}00ms`}} ref={ref}>
                    {navLi}
                  </span>
                </CSSTransition>
              )
            })}
          </ul>
          </>
        )}
      </TransitionGroup>
    </nav>
  );
};

export default Navbar;

Here is the CSS for fade/fadedown/navbar:

/*fade styles*/

.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  opacity: 1;
  transition: opacity 300ms ease-in-out;
}

/*fadedown styles*/

.fadedown-enter {
  opacity: 0.01;
  transform: translateY(-20px);
  transition: opacity 300ms ease-in-out, transform 300ms ease-in-out; 
}

.fadedown-enter-active {
  opacity: 0.01;
  transform: translateY(-20px);
  transition: opacity 300ms ease-in-out, transform 300ms ease-in-out;
}

.fadedown-enter-done {
  opacity: 1;
  transform: translateY(0px);
  transition: opacity 300ms ease-in-out, transform 300ms ease-in-out;
}

/*NAVBAR*/

.navbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px 40px;
  z-index: 10;
  /*slide on scroll up animation*/
  transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
  background-color: rgba(10, 25, 47, 0.5);
  backdrop-filter: blur(10px);
}

.visible {
  transform: translateY(0);
  opacity: 1;
  position: sticky;
  top: 0;
  box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.2);
}

.visible.at-top {
  box-shadow: none;
}

.hidden {
  transform: translateY(-100%);
  opacity: 0;
  position: sticky;
  top: 0;
}

.logo {
  font-size: 20px;
  font-weight: bold;
  color: var(--primary-font-color);
}
 
.navbar > ul {
  display: flex;
  align-items: center;
  list-style: none;
  font-size: 14px;
}

.navbar-list  li:last-child {
  margin-left: 25px;
}

.navLink-item {
  color: var(--primary-font-color);
  margin: 0px 10px;
  font-family: 'Roboto Mono', monospace;
}

.navLink-item:hover {
  color: var(--code-font-color);
  cursor: pointer;
  transition: color .3s;
}

Here are some pictures about the nature of the problem

1st - it loads all the elements at once as soon as 'isMounted' changes to 'true'

2nd - it applies the classes fromCSSTransitions causing the transition to play backwards elements fade away and dissapear

3rd - it plays the animation as it should elements fade down as they should

0

There are 0 answers