Search code examples
javascriptcssreactjssasscss-animations

Add Animation/Transition to Element When Removed From DOM


I have a React component for an Error Message element ErrorMessage that either returns a div element with child elements or returns null so it gets removed from the DOM.

I was curious if there was a way with CSS/React to animate this element as it gets removed from the DOM? I can animate when it renders fine.

Here is a CodeSandBox to test it.

Here is my SASS

.ErrorMessages {
  background: #ee4900;
  color: #fff;
  border: 0;
  color: #fff;
  padding: 0.8em 1em;
  text-align: left;
  font-size: 12px;
  margin-top: 10px;
  margin-bottom: 10px;
  animation-duration: 0.5s;
  animation-fill-mode: both;
  animation-name: fadeInUp;
}

.ErrorMessages--Centered {
  padding-top: 30px;
  padding-right: 70px;
  padding-left: 70px;
}

.ErrorMessages--fadeOut {
  animation-name: fadeOutDown;
}

@keyframes fadeOutDown {
  from {
    opacity: 1;
    max-height: 72px;
  }

  to {
    opacity: 0;
    padding: 0;
    margin: 0;
    max-height: 0;
    transform: translate3d(0, 50px, 0);
  }
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    padding: 0;
    margin: 0;
    max-height: 0;
    transform: translate3d(0, 50px, 0);
  }
  to {
    opacity: 1;
  }
}

Below is my ErrorMessage component

import React from "react";

const ErrorMessage = props => {
  let err = props.show ? (
    <div className="ErrorMessages ErrorMessages--Centered">
      <h4>{props.errorHeader}</h4>
      <p>{props.errorMessage}</p>
    </div>
  ) : null;
  return err;
};

export default ErrorMessage;

Which renders in the DOM as

<div class="ErrorMessages ErrorMessages--Centered"></div>

Any help would be greatly appreciated!


Solution

  • You can setTimeout and raise an animation flag when props.show changes. Something like this:

    const ErrorMessage = props => {
      const [show, setShow] = useState(props.show);
      const [hide, setHide] = useState(!props.show);
      const timeoutRef = useRef(null);
    
      useEffect(() => {
        if (props.show) {
          setShow(true);
          setHide(false);
          clearTimeout(timeoutRef.current);
        }
        else {
          setShow(false);
          timeoutRef.current = setTimeout(() => setHide(true), props.delay || 1000);
        }
      }, [props.show]);
    
      /* unmount cleanup */
      useEffect(() => () => clearTimeout(timeoutRef.current), []);
    
      let err = !hide ? (
        <div className={'ErrorMessages ErrorMessages--Centered' + (show ? '' : ' ErrorMessages--fadeOut')}>
          <h4>{props.errorHeader}</h4>
          <p>{props.errorMessage}</p>
        </div>
      ) : null;
      return err;
    };