しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

React.js初心者がReactでToDoアプリを作ってみる - その1

今更ながらReactJSを勉強をしたメモです。

ReactJSとは

Reactを動かしてみる

公式サイトに書いてある通りにStarter Kitをインストールしてサンプルを動かしてみました。

Getting Started | React

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
    <script src="./react.js"></script>
    <script src="./JSXTransformer.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/jsx">
      React.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
      );
    </script>
  </body>
</html>

ブラウザで実行されてくれました。

f:id:llcc:20150808155233p:plain

JSのコード中にhtmlタグ入ってるのですが、これはJSXというものがうまくJSのコードになおしてくれているようです。

Reactのチュートリアルをやってみる

createClassを使う

続けて公式サイトのチュートリアルを見ながらタスクリスト作成に挑戦してみました。

Tutorial | React

先ほどのhtmlにJqueryの読み込みを追加してJS部分を下のように書き換えます。

// tutorial1.js
var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        Hello, world! I am a CommentBox.
      </div>
    );
  }
});
React.render(
  <CommentBox />,
  document.getElementById('content')
);

下のような表示になりました。
React.createClassを使うと独自のタグを作れるみたいです。

f:id:llcc:20150808160520p:plain

JSXがない場合

JSXを使わない場合は下のような書き方になるようです。
JSX使う方がはるかに見やすいですね。

<script type="text/javascript">
  var CommentBox = React.createClass({
    render: function() {
      return (
        React.createElement('div', {className: "commentBox"},
        "Hello, world! I am a CommentBox.")
      );
    }
  });
  React.render(
    React.createElement(CommentBox, null),
    document.getElementById('content')
  );
</script>

クラスの入れ子

下のように要素の入れ子もできるようです。

var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <CommentBoxChild1 />
        <CommentBoxChild2 />
      </div>
    );
  }
});
var CommentBoxChild1 = React.createClass({
  render: function() {
    return (
      <div className="commentBoxChild1">
        Child1
      </div>
    );
  }
});
var CommentBoxChild2 = React.createClass({
  render: function() {
    return (
      <div className="commentBoxChild2">
        Child2
      </div>
    );
  }
});
React.render(
  <CommentBox />,
  document.getElementById('content')
);

下のように2つの要素が表示されました。

f:id:llcc:20150808161836p:plain

renderの中身

renderの中身はタグをなくせるのか気になったので調べてみました。
確認した所タグをなくすのはできませんでした。
あとタグはdiv以外でも大丈夫なようです。

// これは動かない
render: function() {
  return (
    Hello world
  );
}
// これも動かない
render: function() {
  return (
    <span>
      Hello
    </span>World
  );
}
// これは動く
render: function() {
  return (
    <span>
      Hello world
    </span>
  );
}

変数を使う

変数を使いたい場合は{}で囲めば良さそうです。

var value = 1;

var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        {value}
      </div>
    );
  }
});
React.render(
  <CommentBox />,
  document.getElementById('content')
);

f:id:llcc:20150808162616p:plain

他要素へ値を渡す

他の要素へ変数を渡す場合は下のようにします。
渡す方はタグの要素として設定します。
受け取る側はthis.propsで受け取れるようです。

var CommentBox = React.createClass({
  render: function() {
    return (
      <div>{this.props.text}</div>
    );
  }
});

React.render(
  <CommentBox text={a: 1} />,
  document.getElementById('content')
);

f:id:llcc:20150808165400p:plain

下のように文字列以外を渡す事もできました。

var CommentBox = React.createClass({
  render: function() {
    return (
      <div>{this.props.text.a}</div>
    );
  }
});

var value = {a: "Hello"}
React.render(
  <CommentBox text={{a: 1}} />,
  document.getElementById('content')
);

リスト表示をする

リストは下のようにmapを使ったら実現できました。

var CommentList = React.createClass({
  render: function() {
    var commentNodes = ["AAA", "BBB"].map(function (component) {
      return (
        <div>
        {component}
        </div>
      );
    });
    return (
      <div className="commentList">
        {commentNodes}
      </div>
    );
  }
});

var CommentBox = React.createClass({
  render: function() {
    return (
      <div className="commentBox">
        <h1>Tasks</h1>
        <CommentList />
      </div>
    );
  }
});

React.render(
  <CommentBox />,
  document.getElementById('content')
);

データとビューのバインディング

データとビューの紐付けは下のようにstateを使う事でできました。
紐付をすればdataが更新された時にビューも自動で更新されます。

試しにsetTimeoutで1秒後にデータの更新をしてみました。

var CommentList = React.createClass({
  render: function() {
    var commentNodes = this.props.data.map(function (component) {
      return (
        <div>
        {component}
        </div>
      );
    });
    return (
      <div className="commentList">
        {commentNodes}
      </div>
    );
  }
});

var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: []};
  },
  componentDidMount: function() {
    self = this;
    setTimeout(function() {
      self.setState({data: [{name: "SSS"}]})
    }, 1000);
  },
  render: function() {
    return (
      <div className="commentBox">
        <h1>Tasks</h1>
        <CommentList data={this.state.data} />
      </div>
    );
  }
});

React.render(
  <CommentBox />,
  document.getElementById('content')
);

ワンテンポ置いてから下のような表示に切り替わりました。

f:id:llcc:20150808215804p:plain

タスクの追加をしてみる

今までやった事を参考にタスクの追加処理を実装してみます。
フォームで保存ボタンを押した時にsetStateで新しい値を保存するようにしました。

var TaskForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    this.props.onSubmit(this.refs);
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="name" ref="name" />
        <input type="submit" value="保存" />
      </form>
    );
  }
});

var TaskList = React.createClass({
  render: function() {
    var taskNodes = this.props.tasks.map(function (task) {
      return (
        <div>
        {task}
        </div>
      );
    });
    return (
      <div className="taskList">
        {taskNodes}
      </div>
    );
  }
});

var TaskBox = React.createClass({
  getInitialState: function() {
    return {tasks: []};
  },
  onSubmit: function(form) {
    this.setState({tasks: this.state.tasks.concat(React.findDOMNode(form.name).value)});
  },
  render: function() {
    return (
      <div className="taskBox">
        <h1>Tasks</h1>
        <TaskList tasks={this.state.tasks} />
        <TaskForm onSubmit={this.onSubmit} />
      </div>
    );
  }
});

React.render(
  <TaskBox />,
  document.getElementById('content')
);

下のように追加できるようになりました。

f:id:llcc:20150808221944p:plain

まとめ

きれいな書き方とかは全然分かってないのですが とりあえずタスクを追加する所までできました。
編集・削除も時間あればやってみようと思います。