Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
48
Help us understand the problem. What is going on with this article?
@halhide

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

More than 5 years have passed since last update.

背景

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内からのアクセスが簡潔になる。

48
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
halhide
rubyとかjavascriptとか。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
48
Help us understand the problem. What is going on with this article?