はじめに
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から取得した場合は下記のようなデータが帰ってきます。
[
{
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'
}
}]
}
]
このままだと例えばですが、コメントしたユーザーの名前をフロント側で編集ができる場合、データが正規化されてないので
全ての同じユーザーの名前を変更する必要があり、非常に辛い作業となってしまいます。
使い方
公式ドキュメントを見ればある程度使い方は書いてありますが、実際に使ってみます。
まずはインストール
npm install normalizr
storeでnomalizrを使用する
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
を使用してみてください。