React.js + ES6 + WebPack チュートリアル (Flux編)

[前回の続き](http://qiita.com/tfutada/items/fe55d1c1b0904b5ced2b)

**Flux**とはデザインパターンの一種で、データが**一方向に流れる**(flux)パターンです。

`React.js`単体では、Viewを担う**コンポーネント**だけです。
そこでFluxでは、**アクション**、**ディスパッチャー**、**ストア**を新たに導入します。

データが一方向に

> アクション -> ディスパッチャー -> ストア -> **コンポーネント** -> アクション

と流れていきます。

## Fluxの設定

Fluxをインストールします。

```bash
npm -S install flux
```

## [Chap 9 サンプルアプリの準備](https://www.youtube.com/watch?v=PvjNglsyOHs)

このチュートリアルでは、Todoリストを一覧で表示するアプリを作成します。
(Todoの追加もできる。)

![Screen Shot 2016-06-12 at 8.49.26 AM.png](https://qiita-image-store.s3.amazonaws.com/0/75231/0e4fdddd-2861-7b12-89b3-fce38e6dbd70.png)

> コンポーネント: Page1.js(修正), Todo.js
> ストア : TodoStore.js
> ディスパッチャー: dispatcher.js
> アクション: TodoAction.js

**クラス図もどき**

![Screen Shot 2016-06-12 at 10.47.10 AM.png](https://qiita-image-store.s3.amazonaws.com/0/75231/71fd4410-7011-6f7e-eb77-a83822af9f6f.png)

まずはじめに、**ストア**`TodoStore.js`と**コンポーネント**`Page1.js` `Todo.js`を作成します。
(まだ、アクション、ディスパッチャーは作りません。)

ストアがTodoリストのデータ`todos`を保持します。
Todoリストを取得する`getAll()`インターフェースをコンポーネントに提供します。

```js:TodoStore.js
import { EventEmitter } from "events";

class TodoStore extends EventEmitter {
    constructor() {
        super();
        this.todos = [ // Todoリストを保持します。
                {
                    id: 10,
                    text: "Go Shopping",
                    complete: false
                },
                {
                    id: 20,
                    text: "Pay Bills",
                    complete: false
                },
            ];
    }
    // Todoリストを返します。
    getAll() {
        return this.todos;
    }
}

const todoStore = new TodoStore;

export default todoStore;
```

```js:Page1.js
import React from "react";
import Todo from "./Todo";
import TodoStore from "./TodoStore";

export default class Page1 extends React.Component {

    constructor() {
        super();
        this.state = {
            todos: TodoStore.getAll(), // Todoリストをロードします。
        };
    }

    render () {
        const {todos} = this.state;

        // mapで Todoエレメントを動的に作成。
        const todoList = todos.map((todo) => {
            return <Todo key={todo.id} {...todo}/>; // text={todo.text} complete={todo.complete}
        });

        return (
            <div>
                <h3>My Todo List</h3>
                <ul>{todoList}</ul>
            </div>
        );
    }
}
```

Todoリストの1アイテムを表現するコンポーネント

```js:Todo.js
import React from "react";

export default class Todo extends React.Component {

    render () {
        const { complete, text } = this.props;

        return (
            <li>
                <span>{complete}</span>
                <span>{text}</span>
            </li>
        );
    }
}
```

:arrow_forward: 実行します。上のスクショのように、Todoリストが表示されればOKです。

## [Chap 10 ストアのイベント処理](https://www.youtube.com/watch?v=bvEC6i7CUyE)

ストアでイベントを発生させ、コンポーネントでイベントを受け取ります。

新たに、Todoを追加するインターフェース`addTodo(text)`を作成します。
追加後、イベントを発生させます。

```js:TodoStore.js
class TodoStore extends EventEmitter {
...
    // Add a new Todo to TodoStore.
    addTodo(text) {
        const id = Date.now(); // ユニークIDを適当に採番
        // Todoを新規追加
        this.todos.push({
            id,
            text,
            complete: false,
        });

        this.emit("change"); // イベントを発生
    }
    
    getAll() {
        return this.todos;
    }
}

const todoStore = new TodoStore;

window.td = todoStore; // デバッグ用

export default todoStore;
```

イベントリスナーを登録します。
イベントを受け取ったら、ストアからTodoリストをリロードします。
ステートが変わるので、子コンポーネント`Todo`も自動更新されます。

```js:Page1.js
export default class Page1 extends React.Component {
...
    componentWillMount() { // ビューに初めて描画されるときに一回だけ呼ばれます。
        TodoStore.on("change", () => { // イベントを受け取る
            this.setState({
                todos: TodoStore.getAll(), // Todoリストをリロード
            });
        });
    }

    render () {
...
```

:arrow_forward: とりあえず、コンソールからデバッグします。
`td.addTodo("new task!")`
と入力します。
スクショのように、Todoリストが追加されればOKです。

![Screen Shot 2016-06-12 at 9.19.29 AM.png](https://qiita-image-store.s3.amazonaws.com/0/75231/36516db2-5ce3-0450-46ab-50efdb50da09.png)

## [Chap 11 ディスパッチャー](https://www.youtube.com/watch?v=MZfCVq5iCBw)

ディスパッチャー`dispatcher.js`を新規作成します。

```js:dispatcher.js

import {Dispatcher} from "flux";

export default new Dispatcher;
```

```js:TodoStore.js
import { EventEmitter } from "events";
import dispatcher from "./dispatcher"; // ディスパッチャーのインポート

class TodoStore extends EventEmitter {

...

    getAll() {
        return this.todos;
    }

    // ディスパッチャーからアクションが渡されます。
    handleActions(action) {
        console.log("TodoStore received an action", action);
        switch (action.type) { // 追加のアクション`CREATE_TODO`か？
            case "CREATE_TODO": { // 自分で勝手に決めた定数
                this.addTodo(action.text); // アクションを追加する。で、イベント発生。
            }
        }
    }
}

const todoStore = new TodoStore;

window.td = todoStore; // デバッグ用
// ディスパッチャーにコールバックを登録
dispatcher.register(todoStore.handleActions.bind(todoStore));
window.dp = dispatcher; // デバッグ用

export default todoStore;
```

:arrow_forward: 再度、コンソールからデバッグします。
`dp.dispatch({type:"CREATE_TODO", text:"new task!"})`
と入力します。
Todoリストが追加されればOKです。

![Screen Shot 2016-06-12 at 9.46.49 AM.png](https://qiita-image-store.s3.amazonaws.com/0/75231/0b95b7c5-2d3d-6bbb-d70d-a88c62f25aee.png)

## [Chap 12 アクション](https://www.youtube.com/watch?v=0yW7C22ooos)

最後に、アクションを作成し、コンポーネントとディスパッチャーの橋渡しをします。

アクション`TodoAction.js`を新規作成します。

```js:TodoAction.js
import dispatcher from "./dispatcher";

export function addTodo(text) {
    // ディスパッチャーを呼びます。
    dispatcher.dispatch({type:"CREATE_TODO", text});
}
```

コンポーネントに追加ボタンを設置して、アクションを呼びます。`addTodo(text)`

```js:Page1.jsp
import * as TodoAction from "./TodoAction" // アクションをインポート

export default class Page1 extends React.Component {
...
    addTodo() {
        TodoAction.addTodo("New Task!"); // アクションを呼びます
    }

    render () {
        const {todos} = this.state;

        const todoList = todos.map((todo) => {
            return <Todo key={todo.id} {...todo}/>; // text={todo.text} complete={todo.complete}
        });

        return (
            <div>
                <button onClick={this.addTodo.bind(this)}>Add!</button>
                <h3>My Todo List</h3>
...
```

:arrow_forward: 実行します。`Add!`ボタンを押します。

Todoリストが追加されればOKです。

![Screen Shot 2016-06-12 at 10.06.12 AM.png](https://qiita-image-store.s3.amazonaws.com/0/75231/5c194e2f-8330-5a07-7c32-cdc8cccec8f0.png)