React+Fluxxor+ajaxでfluxなTODOアプリ

  • 15
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Web+DB Pressのvol86と87で伊藤直也さんのReact.js+Fluxの記事があったので勉強してみました。

vol86のReactだけだと外部のAPIとかとの連携のイメージがつきにくかったんですけど、vol87の記事のサンプルを動かしてみてやっと
とはいえまだまだですが汗

やったこと

Reactに慣れてからfluxへという流れです。

まずはTODOアプリを作る

サンプルはこちらに置いておきます。 https://github.com/n0bisuke/react-study/tree/todo-flux

Flux構成のフレームワークであるFluxxorを使って、vol87の記事では紹介してくれています。

早速ですが、実行するとこんな感じです。

Fluxの解説などはWeb+DB Pressを読みましょうw

Fluxxorにajax的な処理を追加してみる

Web+DB Pressの記事では説明が少し割愛されていた部分です

サーバー上にあるjsonデータをHTTPリクエストで取得してTODOリストとして表示させる作りに変えてみます。ちなみに、サンプルはこちらに置いてきます。 https://github.com/n0bisuke/react-study/tree/todo-flux-ajax

非同期通信系の処理はActionを起点にしましょうとのことでActionから処理を追って説明をしてみます。 (ツッコミ歓迎!!)

0. jQuery読み込みとJSONデータ用意

jQueryの$.ajax()を使ってみます。jQueryのロードはindex.html側に書きました。

index.html
<html>
  <head>
    <title>TODOリスト</title>
  </head>
  <body>
    <div id="app-container"></div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script charset="utf-8" src="dest/app.js"></script>
  </body>
</html>

データはこんな感じです。

todos.json
[{
  "text": "ほげ"
 },
 {
  "text": "ふが"
 }
]

ここにapp.jsxの完成系があるのですが、以下で個別に説明してみます。

1. Action(Action Creator)にlaadTodosメソッドを追加

まず、 Actionは定数で定義された、アクションの識別子{value:1}などの入力値に相当するオブジェクトのペアを指すとのことです。ということでLOAD_TODOS_SUCCESSという識別子を定義しておきます。

7行目

app.jsx
/*省略*/

var constants = {
  ADD_TODO:    "ADD_TODO",
  TOGGLE_TODO: "TOGGLE_TODO",
  LOAD_TODOS_SUCCESS: "LOAD_TODOS_SUCCESS" //追記
};

/*省略*/

57行目付近

メソッド内に$.ajax()での処理を記述してきます。
ajax処理の完了時.done()の処理でthis.dispatch()でdataをDispacher経由でStoreに送ります。

app.jsx
/*省略*/

var actions = {
  //追加
  loadTodos: function(){
    $.ajax({
      url: "./todos.json"
    }).done(function(data){
      this.dispatch(constants.LOAD_TODOS_SUCCESS, {data: data});
    }.bind(this));
  },

  /*省略*/

this.dispatch()内の(constants.LOAD_TODOS_SUCCESS, {data: data}の部分がStoreに対して操作を要求するメッセージ(=アクション)となるみたいです。

.bind(this)を書いておかないと動かないので注意!これWeb+DB Press側のコードミスかも。JSのthisの罠。

2. Storeでアクションとメソッドの紐付け(bind)

先ほどのアクションがStoreに送られた際に実行する処理とその紐付けです。

17行目

app.jsx
/*省略*/
var TodoStore = Fluxxor.createStore({
  initialize: function() {
    this.todoId = 0;
    this.todos = {};
    this.bindActions(
      constants.ADD_TODO,    this.onAddTodo,
      constants.TOGGLE_TODO, this.onToggleTodo,
      constants.LOAD_TODOS_SUCCESS, this.onLoadTodosSuccess //追記
    );
  },

  /*省略*/

this.bindActions()で先ほど定義したLOAD_TODOS_SUCCESSのアクションがStoreにメッセージとして送られて来た際のメソッドを紐付けます。ここではonLoadTodosSuccessメソッドに紐付けています。

36行目付近

app.jsx
  /*省略*/

  //追加
  onLoadTodosSuccess: function(payload){
    payload.data.forEach(function(item){
      var id = ++this.todoId;
      var todo = {
        id: id,
        text: item.text,
        complete: false
      };
      this.todos[id] = todo;
    }.bind(this));
    this.emit('change');
  },

  /*省略*/

onLoadTodosSuccessメソッドの中身です。 idをインクリメントしながらデータを追加していく処理になってます。
payloadさっき(61行目){data:data}の部分が送られてきます。

つまり、http通信でGETした内容(todos.jsonの内容そのまま)です。

Action側で非同期処理を書き、処理完了時点のデータをStoreに送り、Storeの中では非同期処理は行わないようにするとデータフローが明確になって良いとのことです。

47行目this.emit('change');と呼んでいます。Storeが管理するデータに変更があったことをViewに伝えてあげます。
これでViewまで伝えてあげたらあとは通常のTODOアプリと同じです。

3. View側でActionCreatorを発火してあげる

1と2で作った処理のトリガーはView側になります。
今回はViewのロード時にこのアクションを実行します。

80行目

app.jsx
  /*省略*/

getInitialState: function() {
    this.getFlux().actions.loadTodos(); //追加: viewの呼び出し時にロード
    return { newTodoText: "" };
  },

    /*省略*/

view側のgetInitialStateメソッド内でloadTodos()を呼び出してあげて、読み込み時にAjaxでデータを取得する処理の完成です。

まとめ

サーバー側の処理を書くのがダルかったのでPOSTでデータ追加とかはやらなかったけど、同じような雰囲気で書けると思います。

Fluxの流れを理解できた気がする...!

次はMilkcocoaと連携してみたいと思います!
http://qiita.com/n0bisuke/items/f28016de82136a2a4d26