Animation is pretty tough job in React. Normally, when a component mounts, you want to animate the component in like fade-in or slide-up or something.

That’s very easy. When component mounts, mount it with in class which has CSS transition (like opacity 0 →1) and use setTimeout to remove that class once transition is completed. But problem comes when component unmounts. Inside componentWillUnmount life-cycle method, you can add out class to the component but component will immediately unmount before CSS transition completes. You need some mechanism to delay unmounting of the component until CSS transition completes. This is where React Transition Groups (RTG) comes into the picture.

RTG package is open-source, developed by some React enthusiasts and focuses on mounting-unmounting logic of a component at it’s core. It has moved from v1 to v2 recently and there are major differences between two versions. In v1, RTG provided lifecycle method likes componentWillEnter(doneCb) and componentWillLeave(doneCb) using which you could defer the mounting and unmoving of the component. The only thing you need to do is wrap you components inside TransitionGroup component.

But things have changed inside v2 of RTG. You have no more lifecycle methods, instead what you get is status/state of the transition.

I have cloned js-plugin-starter for quick launch of react-app. Read README.md file for installation help.

import { Transition } from 'react-transition-group';

Transition component expects one FaaC (function as a child) component. This function is called with status which could be one of the following.

entering

entered

exiting

exited

To see this in action, we will write simple component.

import React from 'react';

import ReactDOM from 'react-dom';

import { Transition } from 'react-transition-group'; class App extends React.Component {

constructor() {

super(); this.duration = 2000;

this.state = {

in: false

}; setTimeout(() => {

this.setState({

in: true

});

}, 2000); setTimeout(() => {

this.setState({

in: false

});

}, 6000);

} render() {

return (

<Transition

in={ this.state.in }

timeout={ this.duration }

>

{

( status ) => {

return <h1>{status}</h1>;

}

}

</Transition>

);

}

} ReactDOM.render(<App />, document.getElementById('app'));

(preview)

Transition components calls FaaC component with a status which we can utilise to do animation. As you can see from code, Transition component expects least two props, in and duration . in is to tell Transition component when to begin transition. When in is true , transition will start with entering status and end with entered after given duration . Similarly, when in is becomes false, status will change to exiting and change to exited after duration .

But there is a problem here. Our component is already mounted and at exited state because initially in prop was false . Transition component also accept two more props without a value. mountOnEnter prop tells Transition component to mount the component only when status changed to entering or in changes to true . unmountOnExit prop tells Transition component to unmount the component when status changes to exited .

<Transition

in={ this.state.in }

timeout={ this.duration }

mountOnEnter

unmountOnExit

>

...

(preview)

Transition component also call the callback functions when a state changes if they mentioned as props. These are onEnter , onEntering , onEntered , onExit , onExiting and onExited . You can read more about them here. There are are other configurations as well to the Transition component like different durations for entering and exiting stages. You can read about all this at official documentation.

The strategy for animation here is, return a custom component from FaaC component and change className dynamically using status.

<Transition

in={ this.state.in }

timeout={ this.duration }

mountOnEnter

unmountOnExit

>

{

( status ) => {

return <h1

className={ 'fade-transition fade-' + status }>

{status}</h1>;

}

}

</Transition>

Now, we just have to create few CSS classes using status names.

// style.scss

.fade-transition{

transition: opacity 2s;

} .fade-entering,

.fade-exiting{

opacity: 0;

}

(preview)

RTG also provide TransitionGroup component which wraps Transition elements. You can pass appear , enter and exit boolean prop which toggles in prop on inner Transition components. When you have new Transition elements, TransitionGroup takes care of animating on those which are new.

class App extends React.Component {

constructor() {

super(); this.state = {

data: [1,2,3,4,5]

}; setTimeout(() => {

this.setState({

data: [1,2,3,4,5,6,7,8,9]

});

}, 2000);



setTimeout(() => {

this.setState({

data: this.state

.data.slice(4)

});

}, 4000);

} render() {

return (

<TransitionGroup>

{

this.state.data.map( num => (

<Transition

key={ num }

timeout={ 500 }

appear={ true }

mountOnEnter

unmountOnExit

>

{

( status ) => {

return <h5

className={ 'fade-transition fade-' + status }

>{status}</h5>;

}

}

</Transition>

) )

}

</TransitionGroup>

);

}

}

(preview)

appear prop by default is set to false which means initial mounting of the component will not change in prop on inner Transition components. But we want animation right from the mounting phase, we have to set it to true .

But you shouldn’t be using Transition component if you want to achieve animations using CSS classes. Use CssTransition component instead. CssTransition gives you very easy interface to add/remove CSS classes with similar capability of Transition component. Read documentation here and check out simple example. You can also use TransitionGroup component to handle animations of multiple child components. Documentation of these is available here. We will be working with TransitionGroup in this article.

But since we are going to deal with anime.js which works with DOM elements and takes care of animation part, we are going to stick with Transition component as child component for TransitionGroup .

Visit anime.js documentation first, because this is one of the most we written and visually appealing documentation I have ever seen. To install anime.js using npm, use below command.

npm install -S animejs

You can import anime.js into your project like below which will add round 14kb of overhead.

import anime from 'animejs';

Let’s revamp our previous example and style in much better way. Let’s create a simple list with two buttons, one which appends new item to the list and one which removes one item from the end.

(preview)

We are going to get 500ms delay before component unmounts because of TransitionGroup component and Transition component children.

The strategy to apply individual animation to list item is to create a separate ListItem component and pass status as prop. Inside ListItem component, get DOM reference of li element and apply anime.js animation inside componentDidUpdate based on input prop status.

(preview)

targets property of anim function expects DOM element, array of DOM elements or CSS selector string but we have li DOM reference which is fine as well. Using translateX and elasticity , we are controlling how li DOM element should animate.