こんにちは、山本です。

さて、React.jsの二回目です。

今回は、デザイナーさんにとってはとても重要らしいが、我々プログラマにとってはそこまで重要でもないもの。
そう、アニメーションをやってみようと思います。

ドキュメントを読む

さて、どのようにアニメーションを行うのでしょうか?

仮想DOMという考え方だと、アニメーションとはあまり相性は良くなさそうな印象ですが。

で気が付いたのですが、ReactはFacebook謹製というのに、前回はFacebookのドキュメントを殆ど見ていませんでした。
これは、反省すべき点ですね。

なので、今回はFacebookのドキュメントを見てましょう。

https://facebook.github.io/react/docs/animation.html

流石公式、すぐに見つかりました。

なんでも、ReactTransitionGroupという低レベルのものと、ReactCSSTransitionGroupという高レベルのものがあるそうですね。

初めから高レベルのものは求めていないので、低レベルものから初めます。

ReactTransitionGroupを使う

では、ReactTransitionGroupを使って、アニメーションを実装してみます。

ドキュメントを読むと、、、

ReactCSSTransitionGroupの事ばかりで、ReactTransitionGroupについての説明が全然ないですね。

検索しても、このページしか見つからないのでこの情報で進めていきます。

アニメーションはアドオンを使用するので、まずはreactとaddonを統合したスクリプトを読み込むようにします。

https://fb.me/react-with-addons-0.14.7.min.js

<!DOCTYPE html>
<html>
  <head>
    <script src="https://fb.me/react-with-addons-0.14.7.min.js"></script>
    <script src="http://fb.me/JSXTransformer-0.13.3.js"></script>
  </head>
  <body>
    <div id="container"></div>
    <script type="text/jsx">
      var ReactTransitionGroup = React.addons.TransitionGroup;

      var EventTest = React.createClass({
        render: function(){
          return (
            <ReactTransitionGroup component="ul" className="animated-list">
              <li>test</li>
            </ReactTransitionGroup>
          );

        }
      });
      React.render(<EventTest />, document.getElementById('container'));
    </script>
  </body>
</html>

実行していますが、動きません。エラーも吐きません。

生成されたhtmlを見てみます。

<div id="container">
  <ul data-reactid=".0" class="animated-list">
    <li data-reactid=".0.$=10">test</li>
  </ul>
</div>

ちゃんと、ReactTransitionGroupが処理されているように見えますが、目的はアニメーションなので動かないと意味がありません。

これ、予想ですがanimated-listを何処かに定義する必要があるようにみえますが、情報が少なくてよく分かりませんね。

仕方がないので、高レベルの方を先にやってみましょう。

ReactCSSTransitionGroupを使う

      var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;

      var TodoList = React.createClass({
        getInitialState: function() {
          return {items: ['hello', 'world', 'click', 'me']};
        },
        handleAdd: function() {
          var newItems =
            this.state.items.concat([prompt('Enter some text')]);
          this.setState({items: newItems});
        },
        handleRemove: function(i) {
          var newItems = this.state.items.slice();
          newItems.splice(i, 1);
          this.setState({items: newItems});
        },
        render: function() {
          var items = this.state.items.map(function(item, i) {
            return (
              <div key={item} onClick={this.handleRemove.bind(this, i)}>
                {item}
              </div>
            );
          }.bind(this));
          return (
            <div>
              <button onClick={this.handleAdd}>Add Item</button>
              <ReactCSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300}>
                {items}
              </ReactCSSTransitionGroup>
            </div>
          );
        }
      });

      React.render(<TodoList />, document.getElementById('container'));

で、確認をすると。

サンプル

ちゃんと行の追加と削除が出来るようになっていますね。

………、いやそうではなくて、今回の目的はアニメーションなんでと思っていたら、スタイルの指定がありました。

.example-enter {
  opacity: 0.01;
}

.example-enter.example-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.example-leave {
  opacity: 1;
}

.example-leave.example-leave-active {
  opacity: 0.01;
  transition: opacity 300ms ease-in;
}

それを足すと、ようやくアニメーションしました!

サンプル

う~ん、ReactではアニメーションはCSSで行う作りなんですね。

確かに、DOMを操作したJSよりも、CSSで行うほうが動作は早いらしいと聞きます。

もし、アニメーションを全てCSSで行えるようになるのであれば、その辺全てデザイナーにやって頂くことが出来そうですね、とても望ましい展開です。

まとめ

さて、本来であればここからより実践的なアプリを作る、、、といきたいところなんですが。
Reactだからこそ、といったものが全く浮かびません。

マスターとなるデータをDOMでは保持しないというのは、非常に共感出来る考え方でした。
DOMの全交換は遅いので、差分だけ更新というコンセプトも納得のいくものでした。
が、何故か手に馴染みません。

このコンポーネントを前提とした、構造が原因なのでしょうか?

このままでは次の記事を書くことが出来ないので、もう少し調べてみて、理解が深まったらその3を書こうかと思います。
それまでは記事としては保留ですね。
(もしかすると、チュートリアルを一からやってみる方がよかったかもしれません)

あと、まだまだ情報が少ないように感じました。
過去の資産を使うことも出来なくなるでしょうし、仕事で使うとを考えると、先の技術なのかな~とも思いました。