Applying React.js CSS Transitions on initial render

February 05, 2015 - John Cobb

In this post I’m going to explore a quick solution to applying React.js CSS transitions to components when they are first rendered to the DOM by creating an interface component for the ReactCSSTransitionGroup add-on.

What does the ReactCSSTransitionGroup add-on do?

The ReactCSSTransitionGroup is an add-on component for React.js which allows you to perform CSS transitions and animations when a React component enters or leaves the DOM. When a new item is added to a ReactCSSTransitionGroup it receives CSS classes to initiate an enter transition, and when it leaves the DOM it receives the corresponding classes to trigger a leaving transition.

This essentially allows you to apply whatever CSS transitions you want to elements as they enter and leave the DOM.

The problem

Recently while using the ReactCSSTransitionGroup add-on for React.js, I ran into the issue of not being able to apply the enter transitions to the child elements when the component is first rendered to the DOM.

When the component is initially rendered, both the ReactCSSTransitionGroup and all of its child elements appear in the DOM at the same time. However, the ReactCSSTransitionGroup will only apply the appropriate animation classes to any child elements which enter the DOM after the initial render.

To illustrate what I mean, here is the example from the React.js documentation implemented in CodePen.

The components inside the transition group will transition in and out of the page when added or removed as defined by the corresponding css rules, but when you first load the page they do not transition in on the initial render.

A work-around to achieve the desired effect

One way around this is to delay the rendering of child components until after the parent component has mounted to the DOM. This approach is outlined in this StackOverflow answer. This is a pretty simple, yet effective, work-around, and requires adding the following code to the component which is responsible for rendering the ReactCSSTransitionGroup component.


    getInitialState: function() {
        return { mounted: false };
    },
    componentDidMount: function() {
        this.setState({ mounted: true });
    },
    render: function() {
        var child;
        if(this.state.mounted){
            child = (<div> ... </div>);
        }
        return (
            <ReactTransitionGroup transitionName="example">
                {child}
            </ReactTransitionGroup>
        );
    }

How can this solution be improved?

The conditional re-render after mounting is a fine work around, but if I want to use this effect in multiple components then I’m going to be repeating it often. Furthermore, I don’t particularly like the idea polluting the component state with an otherwise needless property.

Ideally I’d like a solution which doesn’t affect my component state, and doesn’t result in repeated code.

Creating an interface component for ReactCSSTransitionGroup

To achieve this, I created an interface component for the ReactCSSTransitionGroup component. It’s usage is identical to the ReactCSSTransitionGroup, however it has an additional prop called transitionAppear which takes a boolean value. This prop will handle the conditional rendering based on mounted state, then simply wraps the elements you want to animate with the regular ReactCSSTransitionGroup add-on component.


    var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

    var ReactCSSTransitionGroupAppear = React.createClass({

        propTypes: {
            transitionName: React.PropTypes.string.isRequired,
            transitionEnter: React.PropTypes.bool,
            transitionLeave: React.PropTypes.bool,
            transitionAppear: React.PropTypes.bool
        },

        getInitialState: function() {
            return {mounted: false}
        },

        getDefaultProps: function() {
            return {
                transitionEnter: true,
                transitionLeave: true,
                transitionAppear: true
            };
        },

        componentDidMount: function() {
            this.setState({ mounted: true });
        },

        render: function (){

            var children;

            if(!this.props.transitionAppear){
                children = this.props.children;
            }
            else{
                if(this.state.mounted){
                    children = this.props.children;
                }
            }

            return(
                <ReactCSSTransitionGroup
                    transitionName={this.props.transitionName}
                    transitionEnter={this.props.transitionEnter}
                    transitionLeave={this.props.transitionLeave}
                >
                    {children}
                </ReactCSSTransitionGroup>
            )
        }
    })

To use this component, I simply include it in my app with the rest of my components and use it in place of the ReactCSSTransitionGroup add-on. Here is a codePen using it in place of the ReactCSSTransitionGroup from the example in the React.js documentation.

Conclusion

While this still obviously isn’t an ideal solution, I think it does provide a cleaner implementation of the most common work-around.

It seems like the ability to apply a transition when the component first appears should be available in the ReactCSSTransitionGroup component soon. There is a pull request on Facebook’s React.js GitHub repo which implements the functionality and it was merged in to the master branch last November. It has yet to make it in to a stable release, but hopefully it’s not too far away. In the meantime, if you’d like to use the wrapper component you can find it on my GitHub page.

Please leave a comment if you have any questions, feedback or suggestions!