LoginSignup
1
0

More than 3 years have passed since last update.

vue+akitaでのステート設計パターン

Last updated at Posted at 2019-12-13

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アーキテクチャーイメージ
1_ZvboOQwyeAjPVKdYmaA1dA.png
登場人物は以下の3種類。

Service

Storeを更新する。
ComponentはServiceのIFを意識するだけ。

Store

上の方で説明済み。

Query

Storeからデータを取得する。Observableにデータの取得ができる。

基本的には以下の流れ。

  1. ComponentからServiceを介してStoreを更新
  2. 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();

redux-devtool
Screen Shot 2019-12-13 at 20.37.41.png

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();

redux-devtool
Screen Shot 2019-12-13 at 20.39.07.png

3.Componentでは1のStoreをwatchして、画面に表示するUserListを決定して更新する。

DEMO

image.mov.gif

こうすることでEntityStoreはフロントエンドでのキャッシュにもなるし、データ構造自体も理解しやすくなるかもしれない。。。と思った。でも、もう少しStoreとEntityStoreは疎結合にすべきな気もする :thinking: 他にもっといい方法があったら誰か教えてください。。。 :sweat_smile:
Github

1
0
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
1
0