Animation on unmount with React and react-transition-group

user9870937 picture user9870937 · Aug 9, 2018 · Viewed 10.9k times · Source

I am working with React, and I'm trying to create a Fade component with React-transition-group to fade in and fade out an element depending on a condition stored in the state: http://reactcommunity.org/react-transition-group/css-transition/

This is what I have right now:

import React from "react";
import ReactDOM from "react-dom";
import { CSSTransition } from "react-transition-group";

import "./styles.css";

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mounted: false
    };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        mounted: true
      });
    }, 10);
  }
  render() {
    return (
      <div className="root">
        <CSSTransition
          in={this.state.mounted}
          appear={true}
          unmountOnExit
          classNames="fade"
          timeout={1000}
        >
          {this.state.mounted ? (
            <div>
              <button
                onClick={() => {
                  this.setState({
                    mounted: !this.state.mounted
                  });
                }}
              >
                Remove
              </button>
              <div>COMPONENT</div>
            </div>
          ) : (
            <div />
          )}
        </CSSTransition>
      </div>
    );
  }
}

Here is the CSS

.fade-enter {
    opacity: 0;
    transition: opacity .5s ease;
}

.fade-enter-active {
    opacity: 1;
    transition: opacity .5s ease;
}

.fade-exit {
    opacity: 1;
    transition: opacity .5s ease;
}

.fade-exit-active {
    opacity: 0;
    transition: opacity .5s ease;
}

When the component is mounted there is a transition in opacity from 0 to 1 with .5s. But when its unmounted, it is not animated: the component dissapear without transition.

I made a sandbox with this component to test it: https://codesandbox.io/s/k027m33y23 I am sure that this is a common case, but I can't find a way to animate the component on the unmount. If anyone has any idea it will be very welcome!

-- EDIT -- As @IPutuYogaPermana said, the conditional rendering inside CSSTransition is not neccesary. So this:

{this.state.mounted ? (
<div>
    <button
    onClick={() => {
        this.setState({
        mounted: !this.state.mounted
        });
    }}
    >
    Remove
    </button>
    <div>COMPONENT</div>
</div>
) : (
<div />
)}

Should be like this:

<div>
    <button
    onClick={() => {
        this.setState({
        mounted: !this.state.mounted
        });
    }}
    >
    Remove
    </button>
    <div>COMPONENT</div>
</div>

The component will be automatically mounted or unmounted depending on the in attribute in the CSSTransition component. Here the final code in a codesandbox: https://codesandbox.io/s/62m86nm7qw

Answer

I Putu Yoga Permana picture I Putu Yoga Permana · Aug 9, 2018

It is expected, because as the state is changed, the animation is not started yet, but the children already gone.

So it is like magically instant disappear. Well we just need to hide it right? remove the conditional render.

I checked, the node removed automatically after animation is done. So no need to use conditional render. Luckily! The code become:

import React from "react";
import ReactDOM from "react-dom";
import { CSSTransition } from "react-transition-group";

import "./styles.css";

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      logoIntro: true,
      mounted: false
    };
  }
  componentDidMount() {
    this.setState({
      mounted: true
    });
  }
  render() {
    return (
      <div className="root">
        <CSSTransition
          in={this.state.mounted}
          appear={true}
          unmountOnExit
          classNames="fade"
          timeout={1000}
        >
          <div>
            <button
              onClick={() => {
                this.setState({
                  mounted: !this.state.mounted
                });
              }}
            >
              Remove
            </button>
            <div>SOME COMPONENT</div>
          </div>
        </CSSTransition>
      </div>
    );
  }
}

ReactDOM.render(<Example />, document.getElementById("root"));