Animation on unmount with React and react-transition-group

13,824

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"));
Share:
13,824
Admin
Author by

Admin

Updated on June 27, 2022

Comments

  • Admin
    Admin almost 2 years

    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

  • Admin
    Admin over 5 years
    Thanks @IPutuYogaPermana You mean that "react-transition-group" doesn't take care of this case, and I have to implement it by myself? Is strange, because is a very common case, right?
  • I Putu Yoga Permana
    I Putu Yoga Permana over 5 years
    well, actually the problem is because the children node is removed, even before the animation started. Need a better workaround tho.
  • Admin
    Admin over 5 years
    Ok, I get that. But then, apart of this solution, which would be the most common way of doing a fade out animation just before unmount?
  • I Putu Yoga Permana
    I Putu Yoga Permana over 5 years
    i update my answer, i just realize, the problem is because the child node has conditional render. Just remove the conditional render. So when animation is done, the children removed automatically.
  • Admin
    Admin over 5 years
    Aaah, so this is the magic with CSSTransition! No need of conditional rendering… good! By the way, even when your code works on CodeSandbox, it fails in the snippet; I don't know if it should work there...
  • I Putu Yoga Permana
    I Putu Yoga Permana over 5 years
    I am not familiar with how SO snippets. Only to show the sample code. Please mark this as accepted answer, if this solve your problem ;) @AdCorrea