# ステート

# 単一ステートツリー

Vuex は 単一ステートツリー (single state tree) を使います。つまり、この単一なオブジェクトはアプリケーションレベルの状態が全て含まれており、"信頼できる唯一の情報源 (single source of truth)" として機能します。これは、通常、アプリケーションごとに1つしかストアは持たないことを意味します。単一ステートツリーは状態の特定の部分を見つけること、デバッグのために現在のアプリケーションの状態のスナップショットを撮ることを容易にします。

単一ステートツリーはモジュール性と競合しません。以降の章で、アプリケーションの状態とミューテーション(変更)をサブモジュールに分割する方法について説明します。

# Vuex の状態を Vue コンポーネントに入れる

ストアにある状態を Vue コンポーネント に表示するにはどうすればよいのでしょう? Vuex ストア はリアクティブなので、ストアから状態を"取り出す"一番シンプルな方法は、単純にいくつかのストアの状態を 算出プロパティ (opens new window) で返すことです。

// Counter コンポーネントをつくってみましょう
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count
    }
  }
}

store.state.count が変わるたび、算出プロパティの再評価が発生し、関連した DOM の更新をトリガーします。

しかし、このパターンでは、コンポーネントがグローバルストアシングルトンに依存してしまいます。 モジュールシステムを使っているとき、ストアの状態を使っているすべてのコンポーネントでインポートが必要です。また、コンポーネントのテストのときにモック化が必要となります。

Vuex は、ルートコンポーネントに store オプションを指定することで (これは、 Vue.use(Vuex) で有効にできます)、すべての子コンポーネントにストアを "注入" する機構を提供しています:

const app = new Vue({
  el: '#app',
  // "store" オプションで指定されたストアは、全ての子コンポーネントに注入されます
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})

ルートインスタンスに store オプションを渡すことで、渡されたストアをルートの全ての子コンポーネントに注入します。これは this.$store で各コンポーネントから参照することができます。 Counter の実装を変更しましょう:

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

# mapState  ヘルパー

コンポーネントが複数のストアのステートプロパティやゲッターを必要としているとき、これらすべてにおいて、算出プロパティを宣言することは繰り返しで冗長です。これに対処するため、算出ゲッター関数を生成し、いくつかのキーストロークを省くのに役立つ mapState ヘルパーを使うことができます:

// 完全ビルドでは、ヘルパーは Vuex.mapState として公開されています
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // アロー関数は、コードをとても簡潔にできます!
    count: state => state.count,
    // 文字列を渡すことは、`state => state.count` と同じです
    countAlias: 'count',
    // `this` からローカルステートを参照するときは、通常の関数を使わなければいけません
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

マップされた算出プロパティの名前がステートサブツリーの名前と同じ場合は、文字列配列を mapState に渡すこともできます。

computed: mapState([
  // map this.count to store.state.count
  'count'
])

# オブジェクトスプレッド演算子

mapState はオブジェクトを返すことに注意しましょう。どうやって、他のローカル算出プロパティと組み合わせるのでしょうか? 通常、最終的にひとつのオブジェクトを computed に渡せるように、複数のオブジェクトをひとつにマージするユーティリティを使わなければいけません。しかし、オブジェクトスプレッド演算子 (opens new window)で、シンタックスをかなり単純にできます:

computed: {
  localComputed () { /* ... */ },
  // オブジェクトスプレット演算子で、外のオブジェクトとこのオブジェクトを混ぜる
  ...mapState({
    // ...
  })
}

# コンポーネントはまだローカルステートを持つことできる

Vuex を使うということは、全ての状態を Vuex の中に置くべき、というわけではありません。多くの状態を Vuex に置くことで、状態の変更がさらに明示的、デバッグ可能になりますが、ときにはコードを冗長でまわりくどいものにします。状態の一部がひとつのコンポーネントだけに属している場合は、それをローカルの状態として残しておくとよいでしょう。あなたは、トレードオフを考慮した上で、あなたのアプリの開発ニーズに合った決定をすべきです。