Vuexでネストしたデータに悩まされている方に知って欲しいnormalizr

# はじめに

Vuexを使用している時に、ネストしたデータを非常に扱いにくいと感じたことはないでしょうか？
今回はそういった悩みを持って開発をしている人向けに`nomalizr`を紹介していきたいと思います。

# normalizrとは

https://github.com/paularmstrong/normalizr
↑が公式のライブラリで
下記のように記載してあります。
>Motivation
Many APIs, public or not, return JSON data that has deeply nested objects. Using data in this kind of structure is often very difficult for JavaScript applications, especially those using Flux or Redux.

>Solution
Normalizr is a small, but powerful utility for taking JSON with a schema definition and returning nested entities with their IDs, gathered in dictionaries.

簡単に要約すると

多くのAPIはオブジェクトが深くネストされているJSONデータを返してくる
このようなデータはFluxやReduxで非常に扱いにくい
Normalizrはスキーマ定義でJSONを取得し、ネストされたエンティティとID一覧を返す強力なものです。

# 具体的にはどう使う？

よくあるユーザーの投稿と投稿に対するコメントいうテーブルを考えてみましょう。

簡易テーブル構造

```
User
    id - integer
    name - string

posts
    id - integer
    title - string
    user_id - integer

comments
    id - integer
    comment - string
    user_id - integer
    post_id - integer
```

投稿一覧をAPIから取得した場合は下記のようなデータが帰ってきます。

```js
[
  {
    id: 1,
    title: 'Post1',
    user: {
      id: 1,
      name: 'Tanaka'
    },
    comments:[{
      id: 1,
      comment: 'comment1',
      user: {
        id:2,
        name: 'yamada'
      }
    }]
  },
  {
    id: 2,
    title: 'Post2',
    user: {
      id: 1,
      name: 'Tanaka'
    },
    comments:[{
      id: 2,
      comment: 'comment2',
      user: {
        id:2,
        name: 'yamada'
      }
    }]
  }
]
```

このままだと例えばですが、コメントしたユーザーの名前をフロント側で編集ができる場合、データが正規化されてないので
全ての同じユーザーの名前を変更する必要があり、非常に辛い作業となってしまいます。

## 使い方
公式ドキュメントを見ればある程度使い方は書いてありますが、実際に使ってみます。

まずはインストール

```bash
npm install normalizr
```


storeでnomalizrを使用する

```js:postsのstore
import { normalize, schema } from 'normalizr'

// user用のエンティティを定義
// entities.usersにuserデータが格納される。
const user = new schema.Entity('users')
// comment用のエンティティを定義
// entities.commentsにcommentデータが格納される。
const comment = new schema.Entity('comments', {
  // commentはuserデータを持っているので下記のように定義
  user,
})
// post用のエンティティを定義
// entities.postsにcommentデータが格納される。
const post = new schema.Entity('posts', {
  // postはuserデータを持っているので下記のように定義
  user,
  // postはcommentを配列で持っているので下記のように定義
  comments: [comment],
})
// APIがpostエンティティの配列を返すようなデータ構造なのでschema.Arrayにpostを入れる
// [post]と書いても良い
const postsSchema = new schema.Array(post)

exports const mutations = {
  // ActionでpostDataをAPIから取得し、setPostsに渡しているイメージ
  setPosts(state, postData) {
    // normalizeを使用して、正規化データを取得
    const posts = normalize(postData, postsSchema)
    state.entities = posts.entities
    state.result = posts.result
  },
}
// 下記のようなgetterを用意して使うようなイメージ
export const getters = {
  postGetter: (state) => state.entities.posts ?? {},
  commentGetter: (state) => state.entities.comments ?? {},
  userGetter: (state) => state.entities.users ?? {},
}

export const state = () => ({
  entities: {
  },
  result: []
})
```

上記のようにデータをいれてあげることで、
state.entities.postsには

```
{
  '1': {
    id: 1,
    title: 'Post1',
    user: 1,
    comments: [
      1
    ]
  },
  '2': {
    id: 2,
    title: 'Post2',
    user: 1,
    comments: [
      2
    ]
  }
}
```

state.entities.usersには

```
{
  '1': {
    id: 1,
    name: 'Tanaka'
  },
  '2': {
    id: 2,
    name: 'yamada'
  }
}
```

state.entities.commentsには

```
{
  '1': {
    id: 1,
    comment: 'comment1',
    post_id: 1,
    user: 2
  },
  '2': {
    id: 2,
    comment: 'comment2',
    post_id: 2,
    user: 2
  }
}
```
というような正規化されたデータが入り、扱いやすくなります。


# 最後に

今までVuexでネストされたデータの扱いに困っていた方は是非`normalizr`を使用してみてください。