reactjsgatsbycss-modulesreact-transition-groupreactcsstransitiongroup

CSSTransition from react-transition-group not applying classes


I'm trying to integrate CSSTransition to my Gatsby site, but it is not applying any of the classes. I'm utilizing CSS modules, and I've got a <div> that serves as the parent that fades in and out, essentially applying the fade effect to this and covering the content while it changes. It's got the class fadEffect. Here is my app-layout component, and the SASS.

AppLayout.tsx

import React, { ReactNode, useState } from 'react';

import { ApiContext } from 'contexts/ApiContext';
import { graphql, StaticQuery } from 'gatsby';
import { TransitionGroup, CSSTransition } from 'react-transition-group';

import { Devtools } from '../devtools/Devtools';
import { Footer } from '../footer/Footer';
import { Header } from '../header/Header';
import s from './AppLayout.scss';

interface AppLayoutProps {
  children: ReactNode;
  location: string;
}

const isDev = process.env.NODE_ENV === 'development';

// tslint:disable no-default-export
export default ({ children, location }: AppLayoutProps) => {
  const [fadeEffectVisible, setFadeEffectVisible] = useState(false);

  const handleFadeEffectEntered = () => {
    setTimeout(() => {
      setFadeEffectVisible(false);
    }, 50);
  };

  return (
    <StaticQuery
      query={`${NavQuery}`}
      render={(data) => (
        <>
          <ApiContext>
            <Header navigationContent={data.prismic.allNavigations.edges[0].node} />

            <CSSTransition
              in={fadeEffectVisible}
              timeout={150}
              classNames={{
                enter: s.fadeEffectEnter,
                enterActive: s.fadeEffectEnterActive,
                enterDone: s.fadeEffectEnterDone,
                exit: s.fadeEffectExit,
                exitActive: s.fadeEffectExitActive,
              }}
              onEntered={handleFadeEffectEntered}
            >
              <div className={s.fadeEffect} aria-hidden="true" />
            </CSSTransition>

            <TransitionGroup component={null}>
              <CSSTransition
                key={location}
                timeout={150}
                classNames={{
                  enter: s.pageEnter,
                }}
              >
                <div className={s.layout}>
                  {children}

                  <Footer navigationItems={data.prismic.allNavigations.edges[0].node} />

                  {isDev && <Devtools />}
                </div>
              </CSSTransition>
            </TransitionGroup>
          </ApiContext>
        </>
      )}
    />
  );
};

const NavQuery = graphql`
  query NavQuery {
    prismic {
      allNavigations {
        edges {
          node {
            ...NotificationBar
            ...NavigationItems
            ...FooterNavigationItems
          }
        }
      }
    }
  }
`;

AppLayout.scss

@import '~styles/config';

:global {
  @import '~styles/base';
}

.layout {
  display: block;
  min-height: 100vh;
}

.pageEnter {
  display: none;
}

.fadeEffect {
  display: none;
  position: fixed;
  z-index: 9;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #fff;
  transition: opacity 0.15s linear;

  &Enter {
    display: block;
    opacity: 0;
  }

  &Active,
  &Done,
  &Exit {
    display: block;
    opacity: 1;
  }

  &ExitActive {
    opacity: 0;
  }
}

I'm happy to provide more details/code if this isn't enough. I'm newish to React and Gatsby, so I'm still learning the lingo. Thanks in advance.


Solution

  • I don't see part of your code where you are updating fadeEffectVisible to true for first CSSTransition and I don't see in property at all on second CSSTransition and I would bet that is your issue. Please take a look at this example from React Transition Group for understanding usage of properties.

    App.js

    function App() {
      const [inProp, setInProp] = useState(false);
      return (
        <div>
          <CSSTransition in={inProp} timeout={200} classNames="my-node">
            <div>
              {"I'll receive my-node-* classes"}
            </div>
          </CSSTransition>
          <button type="button" onClick={() => setInProp(true)}>
            Click to Enter
          </button>
        </div>
      );
    }
    

    Style.css

    .my-node-enter {
      opacity: 0;
    }
    .my-node-enter-active {
      opacity: 1;
      transition: opacity 200ms;
    }
    .my-node-exit {
      opacity: 1;
    }
    .my-node-exit-active {
      opacity: 0;
      transition: opacity 200ms;
    }
    

    When the in prop is set to true, the child component will first receive the class example-enter, then the example-enter-active will be added in the next tick.