復習のためReact.jsでTodoアプリのViewを書いてみました。
このTodoアプリを通して次のことを勉強出来ました。
- stateとpropsの使い方
- bindを使ってコールバック関数内のオブジェクト指定
まだコンポーネント分割の単位をしっかりと見通せてません。
では、書いていきます。
ファイル構造
MyApp
├── index.html
└── react-0.13.3
├── README.md
├── build
│ ├── JSXTransformer.js
│ ├── react-with-addons.js
│ ├── react-with-addons.min.js
│ ├── react.js
│ └── react.min.js
HTML雛形作成
React.jsでReactコンポーネントを作る前にHTMLの雛形を作ります。
<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<script src="./react-0.13.3/build/react.js"></script>
<script src="./react-0.13.3/build/JSXTransformer.js"></script>
</head>
<body>
<div id="myApp"></div>
<script type="text/jsx">
<!-- 今回はここにReactのコードを書く -->
</script>
</body>
</html
divタグ内のmyAppにReactで書いたプログラムをロードします。
myAppにロードするReactのコードは<script type="text/jsx">
と</script>
の間に書く。
##必要なコンポーネント
コードを書き始める前に必要なコンポーネントを考えます。
今回は3つのReactコンポーネントを作ります。
- TodoApp ・・・アプリ全体
- TodoCreator ・・・Todoを新規で追加するため
- TodoList ・・・ Todoリストを描画するため
##TodoAppコンポーネントを描画
<script type="text/jsx">
と</script>
タグの間の一番最後でrenderメソッドで描画するコンポーネントとロードするdivのidを指定します。
React.render(
<TodoApp/>,
document.getElementById('myApp')
);
##TodoAppコンポーネント
###renderメソッドを定義
var TodoApp = React.createClass({
render: function(){
return (
<div className="todoApp">
<TodoCreator/>
<TodoList/>
</div>
);
}
});
TodoCreatorコンポーネントとTodoListコンポーネントを返すTodoAppコンポーネントを定義しています。
###Todoリストの初期値の定義
Todoリストの初期値をTodoAppクラス内で定義します。
テストの為に、sampleというキーと0(未完了/false)の値をセットしています。(後でこの中身を消して空のJSONオブジェクトとして定義します。)
getInitialState: function() {
return {
todos = [{item:"sample", status:0}
}
},
###TodoListコンポーネントの定義
Todoリストを表示するためのTodoListコンポーネントを定義します。
var TodoList = React.createClass({
render: function() {
return (
<ul>
{
this.props.todos.map(function(todo, i){
if (todo.status == 0) {return <li key={i}>{todo.item}</li>}
else {return <li><s>{todo.item}</s></li>}
})
}
</ul>
);
}
});
次にTodoAppコンポーネントからgetInitialStateで定義したtodosのステートを渡すために<TodoList/>
を次のように書き換えます。
// <TodoList /> // 変更前
<TodoList todos={this.state.todos} />
これでtodosの状態をTodoListコンポーネントに渡し、TodoList側でpropsを通じてtodosの中身を参照することになります。
###TodoCreatorコンポーネントの定義
Todoの内容を入力し、Addボタンを押してtodosリストに追加するためのコンポーネントを作ります。
var TodoCreator = React.createClass({
_onAdd: function(){
var newTodo = this.refs.inputText.getDOMNode().value;
this.props.onAdd(newTodo);
},
render: function(){
return (
<div className="TodoCreator">
<input type="text" ref="inputText" placeholder="Input your new to\
do" />
<button onClick={this._onAdd}>Add</button>
</div>
);
}
});
Addボタンが押されたら_onAddメソッドを呼び出します。次に_onAddメソッド内ではgetDOMNodeを使ってtextボックス内の値を取得して、newTodo変数に格納し、propsを通じてnewTodoを引数に親であるTodoAppコンポーネントのonAddメソッドを実行します。
####TodoAppのonAddメソッド
TodoAppコンポーネントにonAddメソッドを追加します。
onAdd: function(newTodo){
this.setState({
todos : this.state.todos.concat({item:newTodo, status:0})
});
},
onAddメソッドの内部でsetStateを実行todosの状態を更新します。
こうすることで、todosのステートが更新され、TodoListの表示が更新されます。
##TodoCreatorコンポーネントの修正
Addボタンを押したときに、入力した内容がちゃんと消えるようにするために加筆します。
var TodoCreator = React.createClass({
getInitialState: function(){
return {
value: ""
}
},
_onAdd: function(){
var newTodo = this.refs.inputText.getDOMNode().value;
this.props.onAdd(newTodo);
this.setState({value: ""});
},
_onChange: function(e){
this.setState({
value: e.target.value
});
},
render: function(){
return (
<div className="TodoCreator">
<input type="text" value={this.state.value} ref="inputText" place\
holder="Input your new todo" onChange={this._onChange}/>
<button onClick={this._onAdd}>Add</button>
</div>
);
}
});
valueという変数をgetInitialStateで定義しておき、inputボックスの中身が変化したら_onChangeメソッドでvalue変数を更新します。Addボタンが押されたら、親コンポーネントのtodosに更新作業を行った後に、value変数の中身をNullに戻しています。
##完了Todoの更新
Todoの完了を示すチェックボックを表示し、完了したものは取り消し線で表示するようにTodoListコンポーネントを修正します。
###Todoの表示部分の変更
render: function() {
return (
<ul>
{
this.props.todos.map(function(todo,i){
if (todo.status == 0) {
return (
<li key={i}>
<input type="checkbox"
onClick={this._onDelete.bind(this,i )}/>
{todo.item}
</li>
)
} else {
return <li key={i}><s>{todo.item}</s></li>
}
},this)
}
</ul>
);
}
###Todoステータスの更新
チェックボックスがクリックされると、まずTodoListコンポーネントの_onDeleteが呼ばれ_onDelete内のthis.props.onDelete
で親であるTodoAppコンポーネントのonDeleteメソッドが実行されています
まず、TodoListコンポーネントの_onDeleteの定義から
_onDelete: function(i){
this.props.onDelete(i);
}
次にTodoAppコンポーネントのonDeleteメソッドを定義します。
onDelete: function(i){
var targetTodo = this.state.todos[i];
targetTodo.status = 1;
this.setState({
todos: this.state.todos
});
},
また、propsで渡すためにTodoAppコンポーネントからTodoListへonDeleteを渡すためにTodoListのロード部分を変更します。
<TodoList todos={this.state.todos} onDelete={this.onDelete}/>
これでindex.htmlファイルをブラウザで表示すると下図のTodo画面が出来上がります。
##全体コード!
<!DOCTYPE html>
<html>
<head>
<script src="./react-0.13.3/build/react.js"></script>
<script src="./react-0.13.3/build/JSXTransformer.js"></script>
</head>
<body>
<div id="myApp"></div>
<script type="text/jsx">
var TodoApp = React.createClass({
getInitialState: function(){
return {
todos: []
}
},
onAdd: function(newTodo){
this.setState({
todos : this.state.todos.concat({item:newTodo, status:0})
});
},
onDelete: function(i){
var targetTodo = this.state.todos[i];
targetTodo.status = 1;
this.setState({
todos: this.state.todos
});
},
render: function(){
return (
<div className="TodoApp">
<TodoCreator onAdd={this.onAdd}/>
<TodoList todos={this.state.todos} onDelete={this.onDelete}/>
</div>
);
}
});
var TodoCreator = React.createClass({
getInitialState: function(){
return {
value: ""
}
},
_onAdd: function(){
var newTodo = this.refs.inputText.getDOMNode().value;
this.props.onAdd(newTodo);
this.setState({value: ""});
},
_onChange: function(e){
this.setState({
value: e.target.value
});
},
render: function(){
return (
<div className="TodoCreator">
<input type="text" value={this.state.value} ref="inputText" placeholder="Input your new todo" onChange={this._onChange}/>
<button onClick={this._onAdd}>Add</button>
</div>
);
}
});
var TodoList = React.createClass({
_onDelete: function(i){
this.props.onDelete(i);
},
render: function() {
return (
<ul>
{
this.props.todos.map(function(todo,i){
if (todo.status == 0) {
return (
<li key={i}>
<input type="checkbox"
onClick={this._onDelete.bind(this,i )}/>{todo.item}
</li>
)
} else {
return <li key={i}><s>{todo.item}</s></li>
}
},this)
}
</ul>
);
}
});
React.render(
<TodoApp/>,
document.getElementById('myApp')
);
</script>
</body>
</html>