Akita とは
AkitaはDatoramaというセールスフォースに買収されたマーケティング会社に開発されてるOSS。
Rxjsで作られた、Angular,React,Vueなどでのステート管理ライブラリ。
流行りのステート管理にはflux実装のReduxやvuexなどあるけれど、正直言って登場人物が多すぎて冗長過ぎる。だからmobxとかもあるけど、それならもはやakitaでいいんじゃないかって気がする。
※ちなみにakitaはchrome extensionのredux-devtoolで状態を追える。
詳しいコンセプトはこちら
Store
Akitaには2種類のStoreが存在する。
Store
Storeはストアの状態を管理する単一なObjectなどを入れるところ。基本的にはコレクションなどは入れるべきではない。
提供されるAPIはsetLoading,setError,destroyのみ。
EntityStore
EntityStoreはデータなどを入れるとCollectionとして管理してくれる。
こちらは特殊でsetLoading,setError,destroyは当然提供されてますが、コレクションの管理もしてくれます。
e.g.
import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
type User = {
id: number;
name: string;
};
interface UsersState extends EntityState<User> {}
@StoreConfig({ name: 'users' })
class UsersStore extends EntityStore<UsersState, User> {
constructor() {
super();
}
}
const usersStore = new UsersStore();
const users = [{ id: 1, name: 'name 1' }, { id: 2, name: 'name 2' }, { id: 3, name: 'name 3' }]
userStore.add(users);
とすると、こんな感じでEntityStoreが管理してくれます。
{
users: {
entities: {
'1': {
id: 1,
name: 'name 1'
},
'2': {
id: 2,
name: 'name 2'
},
'3': {
id: 3,
name: 'name 3'
}
},
ids: [1, 2, 3],
loading: false,
error: null
}
}
Store/EntityStoreへのアクセス
本家より拝借したakitaアーキテクチャーイメージ
登場人物は以下の3種類。
Service
Storeを更新する。
ComponentはServiceのIFを意識するだけ。
Store
上の方で説明済み。
Query
Storeからデータを取得する。Observableにデータの取得ができる。
基本的には以下の流れ。
- ComponentからServiceを介してStoreを更新
- ComponentからQueryでStoreを取得
normalizr+reduxのようなステート設計
ここではredux+normalizrのようなentityとidを分けて保管して、表示するidだけを他で管理するようなUser一覧ページを作ってみる。
- Normalizrを使用したReduxの実装パターン
細かいところはGithubに上がってるので、ソースコード読んでほしい。
1. UserListコンポーネントは表示する情報を管理するこんなStoreを持つ。
import { Store, StoreConfig } from '@datorama/akita';
export interface UserListState {
paging: {
page: number;
total: number | null;
};
ids: Array<number>;
}
export function createInitialState(): UserListState {
return {
paging: {
page: 1,
total: null
},
ids: []
};
}
@StoreConfig({ name: 'userList' })
export class UserListStore extends Store<UserListState> {
constructor() {
super(createInitialState());
}
}
export const userListStore = new UserListStore();
2. APIの結果となるUser情報を管理するEntityStoreをsharedに持つ。
APIで情報を取得する度にここに蓄えた後、上記のStoreも更新する。
import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
import { User } from './user.model';
export interface UsersState extends EntityState<User> {}
@StoreConfig({ name: 'users' })
export class UsersStore extends EntityStore<UsersState, User> {
constructor() {
super();
}
}
export const usersStore = new UsersStore();
3.Componentでは1のStoreをwatchして、画面に表示するUserListを決定して更新する。
DEMO
こうすることでEntityStoreはフロントエンドでのキャッシュにもなるし、データ構造自体も理解しやすくなるかもしれない。。。と思った。でも、もう少しStoreとEntityStoreは疎結合にすべきな気もする 他にもっといい方法があったら誰か教えてください。。。
Github