フロントエンド開発のアーキテクチャである「SPA(Single Page Application)」について、開発に必要となる各種フレームワークの特徴や作り方の違いなどを紹介する連載。今回は、状態管理の方法やCSSの適用、単体テストの実装方法について比較し、ReactとAngular2の違いを明らかにする。
フロントエンド開発のアーキテクチャである「SPA(Single Page Application)」について、開発に必要となる各種フレームワークの特徴や作り方の違いなどを紹介する本連載「モダンなフロントエンド開発者になるためのSPA超入門」。
連載初回の「ゼロから学ぶ! Single Page Applicationの特徴と主なフレームワーク5選」ではSPAの特徴と取り巻く環境、フレームワークを紹介しました。前回は、そのフレームワークの中から、特に注目度が高い「React」「Angular2」を使い、簡単なTODOアプリの実装を通して、それぞれのフレームワークにおけるコンポーネントの実装方法の違いを比較しました。
最終回である今回は、より実践的な知識として、状態管理の方法やスタイルの適用、単体テストの実装方法について比較し、ReactとAngular2の違いを明らかにします。
クライアントでの処理量が増加するSPAアプリケーションでは、クライアントサイドでの状態(データ)管理が必須です。各フレームワークでは状態管理をするための仕組みが用意されていますので、React、Angular2それぞれにおける状態管理の方法を説明します。
併せて、Reactによる状態管理でよく使われるフレームワークであるReduxについても紹介します。
Reactの場合、各コンポーネントで状態を保持できますが、コンポーネントの再利用性を高めるために、なるべく状態を持たない(ステートレス)コンポーネントにすることが望ましいです。そのため、原則、親のコンポーネントで状態管理を行い、子コンポーネントは状態を持たないようにします。
本アプリケーションでは、親コンポーネントはApp.jsとして定義し、その中に複数の子コンポーネントを持つ構造となっていました。
そのため、Todoの状態などのデータについては親のApp.jsで管理し、子コンポーネントに受け渡しています。
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: []}; } // 追加アクション addTodo(text) { const { todos } = this.state; this.setState( { todos: [...todos, { id: todos.length, text: text, isChecked: false }] } ); } // チェックアクション 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;
コンポーネントで保持する状態は、Reactが提供するstateに格納されます。Todoデータを格納するstateを用意するため、コンストラクタでstateの初期化を行います。
constructor(props) { super(props); // 関数のバインド this.addTodo = this.addTodo.bind(this); this.checkTodo = this.checkTodo.bind(this); // stateの初期化 this.state = {todos: []}; }
次に、stateの更新方法について説明します。
addTodo(text) { const { todos } = this.state; this.setState( { todos: [...todos, { id: todos.length, text: text, isChecked: false}] } ); }
データの追加については、Reactが提供するsetState関数を使ってstateの更新を行います。Todoを追加する処理では、stateに格納されているtodos配列に対して、新規に作成したTodoデータを追加しています。
addTodo関数はAddコンポーネントで使用するため、propsの仕組みを使って、コンポーネントのレンダリングの際にAddコンポーネントに関数を受け渡しています。
render() { const { todos } = this.state; // AddコンポーネントとTodoListコンポーネントの配置 return ( <div className="App"> <Add addTodo={this.addTodo} /> <TodoList todos={todos} checkTodo={this.checkTodo} /> </div> ); }
Addコンポーネントでは、親から受け取ったaddTodo関数を登録ボタン(inputタグ)のonClickにセットすることで、Todoの追加処理を呼び出せるようにしています。
import React, { Component } from 'react'; import '../styles/add.css'; class Add extends Component { render() { const { addTodo } = this.props; return ( <div className="add"> <h2 className="title">TODOサンプルアプリケーション</h2> <input type="text" className="inputBox" placeholder="TODOを入力" ref={(ref) => this.inputBox = ref} /> <input type="button" className="addBtn" value="登録" onClick={() => { addTodo(this.inputBox.value); this.inputBox.value = ''; }} /> </div> ); } } export default Add;
Copyright © ITmedia, Inc. All Rights Reserved.