Prerequisites

I am designing a transition component which only cares about open/close method from its parent and update the class + data-transition-state on its own element. Let's call it XWindow, and its controller is XWindowManager

XWindow

var XWindow = React.createClass({
    getInitialState: function() {
    return {
        transition: 'closed',
      animation: ''
    }
  },
  onAnimationEnd: function() {
    this.setState({
        transition: this.state.transition === 'closing' ? 'closed' : 'opened';
    });
  },
  componentDidMount: function() {
    this.getDOMNode().addEventListener('animationend', this.onAnimationEnd.bind(this), false);
  },
  open: function(animation) {
    this.setState({
        transition: 'opening',
      animation: animation
    });
  },
  close: function(animation) {
    this.setState({
        transition: 'closing',
      animation: animation
    });
  },
  render: function() {
    return <div className={"x-window " + this.state.animation} data-transition-state={this.state.transition}>
                     {this.props.children}
                 </div>
  }
});

XWindowManager:

var XWindowManager = React.createClass({
    getInitialState: function() {
    return {
        windows: []
    }
  },
  openXWindow: function(content) {
    var contents = this.state.windows;
    contents.push(content);
    this.setState({
        content: contents
    });
  },
  render: function() {
    return <div className={"x-window " + this.state.animation} data-transition-state={this.state.transition}>
                     {this.props.children}
                 </div>
  }
});

App

var app = React.render(<XWindowManager />, document.body);
app.openXWindow(<Inbox />); // Inbox, Thread was defined somewhere.

app.openXWindow(<Thread />);

How to avoid re-rendering Inbox when Thread is opened?

Usally, whenever the top most component is updated by setState, every sub component will be updated, too. React won't perform the real DOM operation if it founds the newly Virtual DOM is the same as the existing Virtual DOM. However, I just don't want any sub component to even compare the Virtual DOM nor to compare the next state and the current state. It's really unneccessary.

How about to tell the children please don't update during the transition?

This just solves part of the problem. Whenever a XWindow is closed and then opened again, the subcomponent of the XWindow will be updated.
Also, it makes the implementation of the child component become dirty: needs to know the parent's transition state to do the update instead of only its own state.

Use CSSTransitionGroup in React.addon

React has a CSSTransitionGroup component to help us to do the transition; however, it does not solve this problem as well. Every children in CSSTransitionGroup will be asked to update once a new children is added.

Final simple answer: Create a Gap Component Between Transition Component and Content!

The solution I come up with is pretty simple and make thing really clear:
to implement a gap component which will always not update.

var ShadowWindow = React.createClass({
    shouldComponentUpdate: function() {
    return false;
  },
  render: function() {
    return <div className="shadow-window">
                     {this.props.children}
                 </div>
  }
});

Then we just need to insert the gap component between XWindow and the content:

// Modify XWindow

  render: function() {
    return <div className={"x-window " + this.state.animation} data-transition-state={this.state.transition}>
                     <ShadowWindow>{this.props.children}</ShadowWindow>
              </div>
  }

Now the XWindow and the content is totally separated. No matter how we transition the XWindow, the content won't be asked to update anymore. It could live with its own state.

Hope this helps :)

Comments

comments powered by Disqus