59
46

More than 5 years have passed since last update.

normalizrを使ったreduxプロジェクト

Last updated at Posted at 2016-05-08

背景

reduxとnormalizr使ってます、という話を勉強会でした時に、いまいち実装イメージが思い浮かばないというフィードバックをもらったので、TODOアプリで例を作ってみた。

ソースはこちら:https://github.com/hokuma/redux-normalizr-todo

TL;DR

  • APIのレスポンスでリソースがネストしている時に効果的
  • entities(ローカルDB)とresult(データの集合)でreducerを分ける
  • entitiesはImmutable.Record使う(おまけ)

題材

よくあるTODOアプリだとリソースはTODOしか出てこない。リソースが一つしかなければそもそもReact単体でやったら、という話になるので、TODOを実行する人をアサインできる機能を持ったTODOアプリを題材にした。

書くこと

reducerの切り方、normalizrの使い方、entityの持ち方。

書かないこと

action周り。

リソース

扱うリソースは以下の通り。

/todos.json
{
  "todos":[
    {
      "todo":{
        "id":1,
        "body":"ticket #1",
        "status":1,
        "userId": 1,
        "user":{
          "id":1,
          "name":"john"
        }
      }
    }
  ]
}
/users.json
{
  "users":[
    {
      "user":{
        "id":1,
        "name":"john",
        "status":1
      }
    }
  ]
}

クライアント側での描画待ちを減らしたい、などの理由で一つのAPIレスポンス内でリソースがネストするようなケースは実際あるだろう。

normalizr

リンク先を見ればわかるが、ネストしたjsonをentityとresultに分解してくれるライブラリ。

entityはデータの実体で、データベースの1レコードに相当すると考えれば良い。resultは意味を持ったデータの集合だが、実データは持たずentityへの参照のみを持つ。この辺は説明よりもリンク先の例を見た方がわかりやすい。

実体とそれへの参照に分けるところがポイントになる。

One Fact in One Place

をクライアント側でもやる感じ。

reducer

entitiy用、result用のreducerを作る。entity用はユーザ用、TODO用とそれぞれネストしていく。result用も同様で、ユーザ一覧、TODO一覧用とネストする。

  • entitiy用のreducerはクライアント側のテーブル操作を行うreducer
  • result用のreducerはアプリとして見せたい情報のまとまり(ユーザ一覧、TODO一覧)操作を行うreducer

と見ることができる。

entities

entities/以下にそれぞれのリソースごとに定義する。

アクションごとに適用するデータ処理を定義する。データ取得系では、基本的にはサーバから受け取った値を最新の値とみなし、既存データを更新 or なければローカルのstoteに追加する。これをまとめてmerge処理として実装する。

状態更新系では、例えばfinishのような状態変化の操作を定義し、対象のtodoのstatusを更新する。

result

entities/以外のreducerは、基本的にはresultを処理するreducerとなる。

取得したデータ or 追加したデータのentityIdをリストへマージするのが主な役割。finishのようなentityそのものを変える処理は扱わない(entityのreducerで処理する)。

Immutable.js

entity、resultどちらもImmutable.jsで管理する。

オブジェクトや集合を扱うためのAPIが豊富で、かつ実行結果が新しいオブジェクトになるのでデータ更新の副作用に起因するバグを減らせる。

データ型がすでにいくつか定義されており、使いようによってはなかなか便利。例えば、resultでOrderedSetを使っている。Setなので同一の値は勝手にユニークが取られ、さらにOrdered、つまり追加した順番を保持してくれるので、一覧表示のためのデータを管理するのに都合が良い。

Immutable.Record

Immutable.Recordを使って、Immutableのインスタンスをクラスのインスタンスっぽく使うこともできる。できることは以下の3つ。

  • 取りうる属性とdefault値を定義できる
  • 属性名でアクセスできる(getを使わなくて良い)
  • 任意のgetterを定義できる

特に2つ目と3つ目のおかげで、Immutable.jsを使いつつも普通のオブジェクトっぽく使えるので、Component内からのアクセスが簡潔になる。

59
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
46