0
  1. I'm displaying a skeleton loader when the page loads.
  2. When My API are done fetching, skeleton is removed from the DOM
  3. Content is added to the DOM.

I would like to fade-out the skeleton when it goes away, and to fade-in the content when it comes in.

For that, I'm using react-transition-group, with the CSSTransition component.

Here is a simplified version of my code so far, please forgive me for the lack of a codepen, since replicating and mimicking API loading/ready states are quite tedious to me :

Homepage.tsx :

function HomePage() {
 
  const { data: dataAPI, error } = useAPI1();
  const { data: dataAPI2, error: error2 } = useAPI2();
  
  if (!dataAPI || !dataAPI2) return <SkeletonLoader isLoading={!dataAPI && !dataAPI2} />;

  if (error) return <Error customMessage="Error from API1" />;
  if (error2) return <Error customMessage="Error from API2" />;

  return (
    <>
      <p>My content !</p>
    </>
  );
}

export default HomePage;

SkeletonLoader.tsx

const SkeletonLoader = ({ isLoading }: { isLoading: boolean }) => {
  const nodeRef = useRef(null);
  return (
    <CSSTransition timeout={1000} classNames="skeleton" in={!isLoading} nodeRef={nodeRef}>
      <Layout>
        <div className={styles.skeleton} ref={nodeRef}>
          <div className={styles.img} />
          <div className={styles.line} />
          <SkeletonProduct />
          <SkeletonProduct />
          <SkeletonProduct />
        </div>
      </Layout>
    </CSSTransition>
  );
};

export default SkeletonLoader;

global.scss

.skeleton-enter {
  opacity: 0;
}

.skeleton-enter-active {
  opacity: 1;
  transform: translateX(0);
  transition: opacity 1000ms;
}

.skeleton-exit {
  opacity: 1;
}

.skeleton-exit-active {
  opacity: 0;
  transition: opacity 1000ms;
}

I've tried different things but I can't see the fade-out when the skeleton is removed from the DOM.

Thank you for your help.

RebootGG
  • 101
  • 9

1 Answers1

1

You're unmounting the transition group before it has time to play your animation.

  if (!dataAPI || !dataAPI2) return <SkeletonLoader isLoading={!dataAPI && !dataAPI2} />;

will stop rendering your transition group.

you could try to always render the transition group, and have it unmount itself with 'unmountOnExit` property:

function HomePage() {
  const { data: dataAPI, error } = useAPI1();
  const { data: dataAPI2, error: error2 } = useAPI2();
  
  const isLoading =  (!dataAPI || !dataAPI2) 
  const shouldRenderContent = !error && !error2 && !isLoading;

  return (
    <>
      { error && <Error customMessage="Error from API1" /> }
      { error2 && <Error customMessage="Error from API2" /> }
      { isLoading && <SkeletonLoader isLoading={isLoading} />}     
      {shouldRenderContent && <p>My content !</p> }
    </>
  );
}

export default HomePage;

....
    <CSSTransition 
       unmountOnExit  // <-- this will remove the children when 'exit' ran.
       timeout={1000}
       classNames="skeleton"
       in={!isLoading}
       nodeRef={nodeRef}
       >
.....

Code sandbox where the api call is replaced with simple button toggle

Lars
  • 3,022
  • 1
  • 16
  • 28
  • Thanks for helping ! That's my bad because I didn't share the full code, but I have several variables before my return which are initiazed with data from my API calls. That means I can't have a single return statement which contains all my loading, error and success cases, because the app will crash before reaching that statement. As you correctly said, I'm unmounting the component before it has time to transition, so I might try another approach like this : https://stackoverflow.com/a/54114180/7354466 – RebootGG Feb 12 '22 at 12:38
  • 1
    @RebootGG , Cheers, I hope my answer was helpful regardless. I can't accurately asses on code i haven't seen, so take my next advice with a grain of salt; i would be wary adding that hook. Sound like you're solving this by band-aiding on more complexity. I think your aim should be instead be to reduce complexity, IE hiding the data usage in a separate hook/useMemo and splitting it up in more child components. – Lars Feb 13 '22 at 21:34