ReactとAngular2の使い方やコードの違いを状態管理、CSS適用、単体テストで比較するモダンなフロントエンド開発者になるためのSPA超入門(終)(2/3 ページ)

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

コラム Fluxアーキテクチャ

 Reactは、あくまでViewを提供するライブラリであるため、たくさんのコンポーネント間で状態を共有するような、複雑な状態管理は本来の役割ではありません。今回のサンプルでは使っていませんが、Reactで大規模な状態管理を実現するための仕組みに「Flux」と呼ばれるアーキテクチャがあります。

図3 Fluxアーキテクチャ(Facebook社のGitHub公式リポジトリから引用)

 FluxアーキテクチャはFacebook社が提唱している状態管理のためのアーキテクチャです。「View」「Action」「Dispatcher」「Store」の4つの役割が存在し、データの流れを1方向に制限するという特徴を持っています。それぞれの役割は表1の通りです。

表1 Fluxアーキテクチャの役割
役割 説明
View データの表示
Action データ更新のためのアクション発行
Dispatcher データ更新のためのイベント通知
Store データの表示管理

 アプリケーションでの状態管理が構造化されることで、処理の役割が明確に分離できます。そのため、役割ごとにソースコードを分割可能で、より大規模な開発がしやすくなります。


コラム Reduxフレームワーク

 Fluxはあくまで考え方であり、Fluxの考えを取り込んだフレームワークが、現在人気のある「Redux」です。

図4 Redux

 ReduxはFluxアーキテクチャの考えを派生させたフレームワークで、「Reducers」と呼ばれる機構が登場するなど、Fluxと少し異なる部分がありますが、考え方はFluxと同様、役割の分割と1方向のデータフローが特徴です。

図5 Reduxフレームワーク

Angular2を使った状態管理

 Angular2では、「サービス」を使って状態を一元管理しています。「サービス」とは、ログ出力やコンポーネントに依存しないビジネスロジックなど、画面から独立してさまざまな箇所で呼ばれる処理を定義するための機構です。

 今回の実装では、「サービス」内の「プロパティ」に各Todoの情報をリストとして保持しています。それぞれのコンポーネントから共通して1つのサービスインスタンスを呼び出すことで、アプリケーションの状態を一元管理しています。

import { Injectable } from '@angular/core';
import { Todo } from '../todo';
@Injectable()
export class TodoService {
  // アプリ内の状態をプロパティで保持
  todos: Todo[];
  constructor() {
    this.todos = [];
  }
  // 追加アクション
  addTodo(title:string): void {
    if(title){
      this.todos.push({ name: title ,enabled: true});
    }
  }
  // チェックアクション
  checkTodo(todo: Todo): void {
    todo.enabled = ! todo.enabled;
  }
}
todo.service.ts

 コンポーネントから「サービス」を呼び出す際は、「DI(Dependency Injection)」の仕組みを利用します。

 DIについては連載第2回で触れましたが、「サービス」を使用するクラス(クライアント)に対して、外部からオブジェクトを注入するデザインパターンです。オブジェクトの生成と使用が分離することにより、柔軟性やテスト容易性が向上するといったメリットがあります。

 Angular2でDIの仕組みを利用するためには、コンポーネント内の「@Component」アノテーション内で、「providers」属性を指定し、サービスプロバイダをインジェクタに登録する必要があります。

@Component({
  selector: 'root',
  templateUrl: './app.component.html',
  providers: [TodoService], // DIするための設定
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  todos:Todo[];
  //コンストラクタでサービスの受け取りが可能となる
  constructor(private service: TodoService) {
    this.todos = service.todos;
  }
}
app.component.ts

 サービスプロバイダが登録された後は、コンストラクタの引数でサービスを受け取ることが可能になります。ここでは、子コンポーネントであるTodoListに渡すための、Todoの一覧をサービスから取得しています。

 今回の実装では、サービス内に状態を変更する関数をまとめて定義しているため、データを更新する際も各コンポーネントからサービスを呼び出す必要があります。

 Todoを追加する機能を持つAddComponentでは、画面から受け取ったinputValueの値をプロパティとして保持し、onClickのタイミングでaddTodo関数を呼び出しています。addTodo関数内ではロジックは保持せずサービスの呼び出しのみを行っています。

<div class="add">
  <h2 class="title">TODOサンプルアプリケーション</h2>
  <input type="text" class="inputBox" placeholder="TODOを入力" [(ngModel)]="inputValue" >
  <input type="button" class="addBtn" (click)="addTodo()" value="登録">
</div>
add.component.html
import { Component, Input } from '@angular/core';
import {TodoService} from '../services/todo.service';
 
@Component({
  selector: 'add',
  templateUrl: './add.component.html',
  styleUrls: ['./add.component.css']
})
export class AddComponent {
  inputValue;
  // コンストラクタで「サービス」の受け取り
  constructor(private service: TodoService) {
  }
 
  // 定義した関数内で「サービス」の呼び出し
  addTodo(): void {
    this.service.addTodo(this.inputValue);
    this.inputValue = '';
  }
}
add.component.ts

スタイル(CSS)の適用

 React、Angular2、それぞれにおけるCSSの適用方法について説明します。

css-modulesを使ったReactにおけるCSSの適用方法

 Reactでは通常のHTML作成と同様、CSSにスタイルを定義する形で開発をすることが可能です。

ul li {
  position: relative;
  padding: 12px 8px 12px 10px;
  font-size: 18px;
  margin-top: 5px;
  border: solid 1px;
  background-color: #ffffff;
  border-radius: 10px;
  box-shadow: 0 10px 6px -6px #777;
}
 
ul li.checked {
  text-decoration: line-through;
}
todo.css

 CSSで定義したスタイルに対して、コンポーネントでは「className」でclassを定義することでスタイルを適用することが可能です。

import React from 'react';
import '../styles/todo.css';
 
const Todo = (props) => {
  const { todo, checkTodo } = props;
  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

 これまでのCSS管理と同様、CSSのスコープがアプリケーション全体となるため、コーディングルールなどを厳格に設けないと「class名がバッティングしてしまう」など、大規模な開発は難しくなります。そこで、「css-modules」と呼ばれる考え方があり、Reactのコンポーネント単位でCSSにスコープを与えることが可能になります。

 スコープがコンポーネント単位となるため、コンポーネントごとに同じclass名を使用することが可能になり、厳格なコーデイングルールが不要となります。その際はモジュールバンドラーと呼ばれる「Webpack」などが提供するCSSを動的に読み込む機能を利用することで、実装が可能です。

Angular2におけるCSSの適用

 Angular-cliでコンポーネントを作成した場合、コンポーネントに対応したCSSファイルが同一のディレクトリ内に生成されるため、そのファイル内にスタイルを書き込むことで適応されます(手動でCSSファイルを作成した場合には、コンポーネント内のstyleUrlsプロパティでCSSとのひも付けを行う必要があります)。

@Component({
  ……
  styleUrls: ['./todo.component.css'] // コンポーネントとCSSファイルとのひも付け
})
export class TodoComponent {
  ……
}
todo.component.ts

 他にも、「スタイルを適用する方法としてテンプレートファイル内に直接styleタグを書き込む」「コンポーネント内にスタイル情報を書き込む」といった方法が提供されています。

 どの方法を使っても、css-modulesの考え方と同様、スタイルはそれぞれのコンポーネントに閉じたスコープにのみ適用されるようになっています。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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