Vuex Store における派生状態の計算:Getter 設計パターン

状態の派生が必要となるケース

アプリケーション開発において、ステートストアに保存されたデータの一部を加工して利用したい状況は頻繁に発生します。例えば、完了したアイテムのリストをフィルタリングし、その総数を取得するような処理です。

これを単一コンポーネント内の計算プロパティで実現する場合、以下のような記述になります:

computed: {
  completedTaskCount () {
    return this.$store.state.inventory.filter(
      item => item.isComplete
    ).length
  }
}

このロジックが複数のビューで使用される場合、コードの重複や外部ファイルへの抽出が必要となり、保守性が低下するリスクがあります。Vuex ではこれを解消するため、ストア内部に「Getter」を設定できます。

Getter の定義と動作原理

Getter はストアのスレッドに対して、依存関係に応じた自動的に更新される計算プロパティとして機能します。依存する値の変更があった場合にのみ再評価が行われ、パフォーマンス効率も考慮されています。

まず、ストア設定内で以下の形式で定義を行います。最初の引数は常に state オブジェクトとなります:

const store = new Vuex.Store({
  state: {
    inventory: [
      { id: 101, name: '製品 A', isComplete: true },
      { id: 102, name: '製品 B', isComplete: false }
    ]
  },
  getters: {
    // 完了しているアイテムのみを取得
    finishedItems: state => {
      return state.inventory.filter(item => item.isComplete)
    }
  }
})

値へのアクセスとチェーン化

定義した Getter は store.getters オブジェクトを通じて公開されます。通常の属性アクセスと同じように読み込むことができます:

store.getters.finishedItems 
// 返却値:[{ id: 101, name: '製品 A', isComplete: true }]

さらに、一つの Getter が別の Getter の結果を利用する連鎖も可能です。これは、前回の計算結果を活用して新たな集計を行う際に効果的です。

getters: {
  // ...finishedItems の定義あり
  finishedItemCount: (state, getters) => {
    // 他の Getter を利用して長さを持つ
    return getters.finishedItems.length
  }
}
// 呼び出し:store.getters.finishedItemCount // → 1

コンポーネント内からは、this.$store.getters.<getter 名> で参照が可能です。Vue のリアクティブシステムにより、Getter の値変更は自動的に UI に反映されます。

引数を指定可能な Getter の作成

特定の条件でデータを検索する必要がある場合、Getter から関数を返却させる手法が有効です。これにより、動的な引数を渡してクエリを実行できます。

getters: {
  getItemById: (state) => (id) => {
    return state.inventory.find(item => item.id === id)
  }
}

// インスタンス呼び出し
store.getters.getItemById(101) 
// 返却値:{ id: 101, name: '製品 A', isComplete: true }

注意点として、このように関数としてアクセスする Getter は、每次都呼び出され、結果がキャッシュされることはありません。

mapGetters によるコンポーネントとの連携

多くの Getter を手動で各コンポーネントの計算プロパティに移行するのは手間です。vuex が提供する mapGetters ヘルパーを使用すると、マッピングを簡素化できます。

配列形式で直接名前を指定して展開することも可能です:

import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters([
      'finishedItemCount',
      'specialData'
    ])
  }
}

既存のプロパティ名とは異なる名前にしたい場合は、オブジェクト形式を使用します。これはコードの可読性を向上させます。

...mapGetters({
  // ローカル名 'totalDone' を store.getters.finishedItemCount に割り当て
  totalDone: 'finishedItemCount'
})

タグ: Vuex vue-framework getter-pattern state-management computation-logic

5月21日 02:51 投稿