connected_user
connected_user

Reputation: 936

ReactCSSTransitionGroup: Elements on their way out stay mounted along with newly mounted elements for a short time

I have a relatively simple use case involving a group of buttons each representing a category.

When one of these category buttons is clicked its corresponding sub-categories will appear in a section below. When you click on one of these category buttons, the list of of sub categories gets replaced with new sub-categories depending on the category you just clicked.

An example could be (if I were to click Category A):

[Category A(Clicked)] [Category B] [Category C]
_______________________________________

[Sub-Category A1] [Sub-Category A2] [Sub-Category A3]

Now the problem happens when I click a different category after already clicking one. I notice that the Sub-Categories of the previous action persist in view for a brief amount of time. For instance this is a snapshot of what happens when I click on Category B after already clicking on Category A.

[Category A] [Category B (Clicked)] [Category C]
_______________________________________

[Sub-Category B1] [Sub-Category B2] [Sub-Category B3] [Sub-Category A1]

Even though I clicked on Category B, and the list of Sub-Categories get replaced, the elements from the previous list of Sub-Categories (Category A) stay mounted for a brief amount of time (in the hundreds of milliseconds).

This only happens when I wrap the sub-categories section in the ReactCSSTransitionGroup tag. My keys for each sub-category is the string name of the sub-category, as they are all unique in my database so I don't think my problem is related to the keys being set.

Here is the code for my ReactCSSTransitionGroup markup:

<ReactCSSTransitionGroup transitionName="products" transitionEnterTimeout={0} transitionLeaveTimeout={0}>
    {   
     this.state.subCategories != null
    ?
     this.state.subCategories.map((subCategoryName, index) => {
        return(
            <div className="product-sub-category" key={subCategoryName}>
                <div className="product-image windows"><div className="overlay"><div className="wrap"><div className="category-title">{subCategoryName}</div><div className="category-subcategories">Sub-Categories: <span className="num">1</span></div></div></div></div>
            </div>)
                        })
    :
     null
   }
</ReactCSSTransitionGroup>

Here is the animation code in my animations css file:

.products-enter {
 opacity: 0.01;
}

.products-enter.products-enter-active {
 opacity: 1;
 transition: opacity 100ms ease-in;
}

.products-leave {
 opacity: 1;
}

.products-leave.products-leave-active {
  opacity: 0.01;
  transition: opacity 100ms ease-in;
 }

Again, this only happens when using ReactCSSTransitionGroup. And it is visually unpleasing. Even though the fades are technically working, it is totally ruined by the fact that old elements are lingering for a short amount of time instead of being unmounted properly.

Upvotes: 0

Views: 97

Answers (1)

john_omalley
john_omalley

Reputation: 1398

That's by design. Fade in/out with ReactCSSTransitionGroup is most appropriate for a list of items that are independently inserted and deleted. For your use case it doesn't work well.

Maybe there's a better library out there, but it's pretty easy to roll your own 'FadeIn' component that simply animates on show, but not hide. It would probably be more appropriate for your use case - here's what i did:

import React, {PropTypes as T} from 'react'
import classnames from 'classnames'

export default class FadeIn extends React.Component {
  constructor(...args) {
    super(...args)
    this.state = {active: false}
  }

  componentDidMount() {
    setTimeout(() => this.setState({active: true}), 0)
  }

  render() {
    const {active} = this.state
    return (
      <div className={classnames('fade-in', {active}, this.props.className)}>
      {this.props.children}
      </div>
    )
  }
}

FadeIn.propTypes = {
  className: T.string
}

FadeIn.displayName = 'FadeIn'

LESS:

.fade-in {
  opacity: 0;
  &.active {
    opacity: 1;
    transition: opacity 0.15s ease-in;
  }
}

Upvotes: 1

Related Questions