ReactとAngular2の使い方やコードの違いをコンポーネント単位で比較するモダンなフロントエンド開発者になるためのSPA超入門(2)(3/4 ページ)

» 2017年04月04日 05時00分 公開
[千葉達仁, 日野達也三菱総研DCS]

Reactコンポーネントの作成

Todoコンポーネント

 まず、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;
Todo.js

 冒頭にJavaScriptでは見慣れない「import」宣言があります。こちらはECMAScript 2015で策定されている仕様で、外部のファイルを参照して利用でき、JavaScriptでの大規模開発がし易くなります。もちろん、そのままではブラウザ上では動作しませんので、スターターツールに組み込まれているモジュール管理ライブラリ「webpack」がファイル間の依存関係を解決し、1つのJavaScriptファイルにバンドルします。

 TODOのテキスト部分は親から受け取った値を表示するようになっており、親からデータを受け取る「props」と呼ばれる仕組みを使って、親コンポーネントから子コンポーネントへデータの受け渡しを行います。

図5 データの流れ

 親の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>
    )
  }
Todo.js ※JSX部分を抜粋

 コンポーネントの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を適用できます。

TodoListコンポーネント

 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;
TodoList.js

 子であるTodoコンポーネントをimportし、for文でTODOの数だけTodoコンポーネントを描画します。

Appコンポーネント

 最後に、アプリケーションの最上位の親コンポーネントにあたる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.js

 Appコンポーネントでは「state」と呼ばれる機能を使ってTODOデータを管理し、子コンポーネントへデータの受け渡しを行っています(データの管理について詳しくは第3回で説明します)。

 画面を構成しているAddコンポーネントとTodoListコンポーネントを配置し、TODOのデータは「todos」オブジェクトにデータを詰めてTodoListコンポーネントへ渡します。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。