まず、ReactではTODOリストの各行を生成しているTodoコンポーネントは次のようになっています。
import React from 'react';
import '../styles/todo.css';
// コンポーネントを宣言
const Todo = (props) => {
// 親コンポーネントから値を受け取る
const { todo, checkTodo } = props;
// チェック済みのTODOか判定
if (todo.isChecked) {
return (
<li className="checked" onClick={() => checkTodo(todo.id)}>{todo.text}</li>
)
} else {
return (
<li onClick={() => checkTodo(todo.id)}>{todo.text}</li>
)
}
};
// コンポーネントをエクスポート
export default Todo;
冒頭にJavaScriptでは見慣れない「import」宣言があります。こちらはECMAScript 2015で策定されている仕様で、外部のファイルを参照して利用でき、JavaScriptでの大規模開発がし易くなります。もちろん、そのままではブラウザ上では動作しませんので、スターターツールに組み込まれているモジュール管理ライブラリ「webpack」がファイル間の依存関係を解決し、1つのJavaScriptファイルにバンドルします。
TODOのテキスト部分は親から受け取った値を表示するようになっており、親からデータを受け取る「props」と呼ばれる仕組みを使って、親コンポーネントから子コンポーネントへデータの受け渡しを行います。
親のTodoListコンポーネントから「todo」オブジェクトの形でデータを受け取り、todoオブジェクト内に格納されている「todo.text」にアクセスし、テキストデータを取り出しています。
if (todo.isChecked) {
return (
<li className="checked" onClick={() => checkTodo(todo.id)}>{todo.text}</li>
)
} else {
return (
<li onClick={() => checkTodo(todo.id)}>{todo.text}</li>
)
}
コンポーネントのView部分の実装は「JSX」と呼ばれるHTMLライクな構文を使って実装します。JSXで記述したコードをReactがDOMに変換し、画面に出力します。JSXではHTMLとほぼ同じタグが記述可能ですが、あくまでHTMLライクな構文であるため注意が必要です。
TODOのリスト部分は単純なul/liタグを使ってリストを作成しているため、子のTodo.jsでは中身のliタグを生成する作りになっています。親であるTodoList.jsからもらったTODOのデータを使い、liタグを生成します。今回は未チェックとチェック済みのTODOで表示を切り替えるため、「isChecked」フラグを使ってJSXの出し分けを行っています。
また、HTMLと異なる点の1つとして「className」があります。JSXではclassを指定するときにはclassNameと記述することで出力されるHTMLにclassを適用できます。
Todo.jsコンポーネントの親であるTodoListコンポーネントは以下の通りです。
import React from 'react';
// コンポーネントを読み込み
import Todo from './Todo';
import '../styles/todoList.css';
const TodoList = (props) => {
// 親コンポーネントから値を受け取る
const { todos, checkTodo } = props;
const list = [];
// TODOデータの数だけリストを作成
todos.forEach((todo, index) =>
list.push(<Todo key={index} todo={todo} checkTodo={checkTodo} />
));
return (
<ul className="todoList">
{list}
</ul>
);
}
// コンポーネントをエクスポート
export default TodoList;
子であるTodoコンポーネントをimportし、for文でTODOの数だけTodoコンポーネントを描画します。
最後に、アプリケーションの最上位の親コンポーネントにあたるAppコンポーネントを見てみます。
import React, { Component } from 'react';
//コンポーネントを読み込み
import Add from './Add';
import TodoList from './TodoList';
import '../styles/index.css';
class App extends Component {
constructor(props) {
super(props);
// 関数のバインド
this.addTodo = this.addTodo.bind(this);
this.checkTodo = this.checkTodo.bind(this);
// stateの初期化
this.state = {todos: []};
}
// TODO追加アクション
addTodo(text) {
const { todos } = this.state;
this.setState(
{
todos: [...todos, { id: todos.length, text: text, isChecked: false }]
}
);
}
// TODOチェックアクション
checkTodo(id) {
const todos = [...this.state.todos];
todos.forEach((todo) => {
if (todo.id === id) {
todo.isChecked = !todo.isChecked;
}
});
this.setState(
{ todos: todos }
);
}
render() {
const { todos } = this.state;
// AddコンポーネントとTodoListコンポーネントの配置
return (
<div className="App">
<Add addTodo={this.addTodo} />
<TodoList todos={todos} checkTodo={this.checkTodo} />
</div>
);
}
}
export default App;
Appコンポーネントでは「state」と呼ばれる機能を使ってTODOデータを管理し、子コンポーネントへデータの受け渡しを行っています(データの管理について詳しくは第3回で説明します)。
画面を構成しているAddコンポーネントとTodoListコンポーネントを配置し、TODOのデータは「todos」オブジェクトにデータを詰めてTodoListコンポーネントへ渡します。
JavaScriptを中心としたWebアプリ開発の栄枯盛衰まとめ――LiveScriptからAngularJS/React.jsまで
いまさら聞けないReact、Virtual DOM、JSX超入門
アメブロでReactやIsomorphic Web Applicationを採用した理由――その成果と構成技術Copyright © ITmedia, Inc. All Rights Reserved.