Using transitionend event listener with react to create a transition

Stephen Agwu picture Stephen Agwu · Sep 5, 2017 · Viewed 10.6k times · Source

I'm trying to make a simple transition with react on a button click where the body max-height goes to 0 on componentWill update, then back to 500px or 100% on componentDidUpdate. I haven't been abe to quite understand it from the other questions I've seen so could someone show me an example with an explanation of how it works?

I also wouldn't mind an example/explanation using reactcsstransitiongroup.

More Info

I understand that transitionend attaches an event listener but what I'm getting confused with is how to use that to ensure the component doesn't update until the transition is finished (I've taught myself react and pretty much all my coding knowledge so I don't know if this is suppossed to be hard to understand, but its hard for me at the moment). Thanks all!

Answer

Stephen Agwu picture Stephen Agwu · Sep 6, 2017

So after some rest I decided it was time for another chapter of 'Student teaches Student' (written directed and starring me).

To anwer my own question more succintly, the transitionend event listener attaches a listener to an element and fires a callback function whenever the transition is over. The problem I was having was that this is async and thus, putting it in componentWillUpdate wouldn't work as the DOM would render before the transition had completed. My current workaround is to have the button click call for a function that contains the event listener, and having the callback function of trnasitionend change the state of the component. This way no rendering is done until the transition is complete.

Code:

class Button2 extends React.Component {
  constructor(props){
    super(props)
    this.onClickHandler = this.onClickHandler.bind(this)
  }

  onClickHandler(){
    this.props.onClick("Button2")
  }

  render() {
    return (
        <button onClick={this.onClickHandler} id="button2">
            BUTTON2!!
        </button>
    )



 }
}

class Button1 extends React.Component {

  constructor(props){
    super(props)
    this.onClickHandler = this.onClickHandler.bind(this)
  }

  onClickHandler(){
    this.props.onClick('Button1')
  }

  render() {
    return (
        <button onClick={this.onClickHandler} id="button1">
            BUTTON1!!
        </button>
    )
  }
}

class App extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      showButton : true,
      hide: false
    }
    this.changeButtonState = this.changeButtonState.bind(this)
    this.getButton = this.getButton.bind(this)
    this.transitionEndEventName = this.transitionEndEventName.bind(this)
    this.hideApp = this.hideApp.bind(this)
    this.removeEvent = this.removeEvent.bind(this)
  }

  getButton() {
    if (this.state.showButton){
        return <Button1 onClick={this.hideApp}/>
      } else {
        return <Button2 onClick={this.hideApp}/>
      }
  }

  transitionEndEventName () {
    var el = document.createElement('div')//what the hack is bootstrap

    var transEndEventNames = {
      WebkitTransition : 'webkitTransitionEnd',
      MozTransition    : 'transitionend',
      OTransition      : 'oTransitionEnd otransitionend',
      transition       : 'transitionend'
    }

    for (var name in transEndEventNames) {
      if (el.style[name] !== undefined) {
        return transEndEventNames[name];
      }
    }

    return false // explicit for ie8 (  ._.)
}


  hideApp(button) {
    var app = document.getElementById('main')
    var transitionEnd = this.transitionEndEventName()
    app.addEventListener(transitionEnd, this.removeEvent, false)
        app.classList.contains('show-element') ? app.classList.remove('show-element') : null
        app.classList.add('remove-element')
  }

  removeEvent(){
    console.log('hey')
    var app = document.getElementById('main')
    var transitionEnd = this.transitionEndEventName()
    app.removeEventListener(transitionEnd, this.removeEvent, false)
    this.changeButtonState()
  }

  changeButtonState(button) {
    this.setState({
      showButton: !this.state.showButton,
      hide: true
    })
  }

  componentDidUpdate(){
    var app = document.getElementById('main')
    app.classList.contains('remove-element') ? app.classList.remove('remove-element') : null
    app.classList.add('show-element')
  }

  render(){
    var button = this.getButton()
    return (
        <div id="button-container">
            {button}
        </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('main'))

codepen (check the codepen