React.jsでAnimation

今回はReact.jsでのAnimationについて書きたいと思います。

React.jsではAddonとしてサポートされていて、CSS Animationを使ったCSSTransitionGroupとComponentのLifecycle MethodのようにMethodでフックして書く2パターンあります。

## CSSTransitionGroup

こちらではComponentの追加・削除時にCSSアニメーションさせることが出来ます。やりかたはAngular.jsやvue.jsのものに近くて追加・削除時にclassが追加されるのでcssにアニメーションを記述する感じです。

`{transitionName}-{enter,leave}`の`className`追加された後、次のイベントループで`{transitionName}-{enter,leave}-active`の`className`が追加されるので、追加される`className`を使ってCSSアニメーションを書いていきます。


```js
var CSSTransitionGroup = React.addons.CSSTransitionGroup;

var Hello = React.createClass({
  getInitialState: function() {
    return {
      value: '(´・ω・｀)'
    };
  },
  onClick: function() {
    var value = this.state.value === '(´・ω・｀)' ? '(｀･ω･´)ゞ' : '(´・ω・｀)';
    this.setState({ value: value });
  },
  render: function() {
    var value = <span className="sample" key={this.state.value}>{this.state.value}</span>; 
    return (
      <div>
        <div>Animation!!<button onClick={this.onClick}>click!!</button></div>
        <CSSTransitionGroup transitionName="sample">
          {value}
        </CSSTransitionGroup>
      </div>
    );
  }
});

React.render(<Hello />, document.body);
```

```css
.sample-enter {
     -webkit-transition: 1s ease-in;
}
.sample-enter.sample-enter-active {
    font-size: 80px;
}
.sample-leave {
    -webkit-transition: .5s ease-out;
}
.sample-leave.sample-leave-active {
    font-size: 10px;
}
```

http://jsfiddle.net/koba04/4L6oLfbg/4/



### 注意点

* アニメーションさせる要素には必ず`key`を指定する必要があります。アニメーションさせる要素が1つの場合でも必ずつける必要があります。これはComponentが追加されたのか更新されたのかを明確に示すためだと思います。
  * これを利用すると、上記の例のように1つのComponentでも`key`を変化させることでアニメーションさせることが可能になります。(Componentの追加・削除が行われるので)

* アニメーションは追加時(enter)と削除時(leave)の両方に指定する必要があります。
  * 追加時、削除時だけにアニメーションさせたい場合は`transitionEnter={false}`や`transitionLeave={false}`を指定することが出来ます。

```js
<CSSTransitionGroup transitionName="sample" transitionLeave={false}>
  {value}
</CSSTransitionGroup>
```

* CSSTransitionGroupのComponentはアニメーション開始時にはrenderingされている必要があって、追加される要素と一緒にCSSTransitionGroupのComponentを追加してもアニメーションしません。

↓の場合は、1回目のclickでは`CSSTransitionGroup`がないためアニメーションしない

```js
var Hello = React.createClass({
  getInitialState: function() {
    return {
      value: ''
    };
  },
  onClick: function() {
    var value = this.state.value === '(´・ω・｀)' ? '(｀･ω･´)ゞ' : '(´・ω・｀)';
    this.setState({ value: value });
  },
  render: function() {
    var value ;
    if (this.state.value) {
      value = ( 
        <CSSTransitionGroup transitionName="sample">
          <span className="sample" key={this.state.value}>{this.state.value}</span>
        </CSSTransitionGroup>
      );
    }
    return (
      <div>
        <div>Animation!!<button onClick={this.onClick}>click!!</button></div>        
          {value}
      </div>
    );
  }
});
```


## ReactTransitionGroup

CSSアニメーションではなくて自分で柔軟に処理を書きたい場合は、ReactTransitionGroupを使うことが出来ます。
`componentWillEnter(callback)`、`componentDidEnter()`、`componentWillLeave(callback)`、`componentDidLeave()`という4つのメソッドがLifecycleに応じて呼ばれるようになるので実装します。

またReactTransitionGroupはdefaultだとspan要素としてDOMに追加されるのですが、`<ReactTransitionGroup component="ul">`などとすることで要素を指定することが出来ます。

```js
var TransitionGroup = React.addons.TransitionGroup;
var duration = 1000;
var AnimationComponent = React.createClass({
  componentWillEnter: function(callback) {
    console.log("component will enter");
    $(this.getDOMNode()).hide();
    callback();
  },
  componentDidEnter: function() {
    $(this.getDOMNode()).show(duration);
    console.log("component did enter");
  },
  componentWillLeave: function(callback) {
    console.log("component will leave");
    $(this.getDOMNode()).hide(duration, callback);
  },
  componentDidLeave: function() {
    console.log("component did leave");
  },
  render: function() {
    return <div>{this.props.text}</div>
  }
});

var Hello = React.createClass({
  getInitialState: function() {
    return {
      value: '(´・ω・｀)'
    };
  },
  onClick: function() {
    var value = this.state.value === '(´・ω・｀)' ? '(｀･ω･´)ゞ' : '(´・ω・｀)';
    this.setState({ value: value });
  },
  render: function() {
    var value = <AnimationComponent key={this.state.value} text={this.state.value} />; 
    return (
      <div>
        <div>Animation!!<button onClick={this.onClick}>click!!</button></div>
        <TransitionGroup>
          {value}
        </TransitionGroup>
      </div>
    );
  }
});

React.render(<Hello />, document.body);
```

http://jsfiddle.net/koba04/hr5vkteL/5/

### 注意点

* `componentWillEnter`と`componentWillLeave`は処理が終わったら必ず`callback`を呼ぶ必要があります。


------

というわけでアニメーションについて簡単に説明しました。
アニメーション書くのに慣れてないせいかちょっと書きにくい感があって、今後の改善点にもAnimationは挙げられているのでもっとわかりやすく簡単に書けるようになるといいなと思っています。

明日はserver-side renderingについて書きたいと思います。