web-dev-qa-db-ja.com

Vuex 2で初期状態を設定するにはどうすればよいですか?

小さなアプリにはVue.js 2.0とVuex 2.0を使用しています。 APIから初期状態を取得するアクションを呼び出すことで、ルートVueインスタンスの「作成された」ライフサイクルフックでストアを初期化しています。..ルートコンポーネント

const app = new Vue({
 el: "#app",
 router,
 store,
 data: {
     vacation: {},
 },
 components: {
    'vacation-status': VacationStatus,
 },
 created(){
    //initialize store data structure by submitting action.
    this.$store.dispatch('getVacation');
 },
 computed: {
 },
 methods: {
 }
});

これはうまく機能しています。ここに私が呼んでいる私の店でのアクションがあります:

getVacation({commit}){
  api.getVacation().then(vacation => commit(UPDATE_VACATION, vacation))
}

これが「UPDATE_VACATION」でコミットしている変異は次のとおりです。

[UPDATE_VACATION] (state, payload) {
  state.vacation = payload.vacation;
},

私の問題:アプリをロードすると、ストアから値を取得しているすべてのコンポーネントがエラーをスローし、ストアの「未定義」値にアクセスしようとしています。つまり、状態はまだ初期化されていません。

たとえば、次のようなChild Componentsにゲッターを持つコンポーネントがあります。

computed: {
        arrival () {
            return this.$store.getters.arrival
        },
        departure() {
            return this.$store.getters.departure
        },
        countdown: function() {
            return this.$store.getters.countdown
        }
} 

'vacation'は状態オブジェクトで定義されていないため、これらのゲッターはすべてエラーを引き起こします。非同期の問題のように思えますが、間違っている可能性があります。 ストアの状態を間違った場所で初期化していますか?

  Vue.use(Vuex);

  export default new Vuex.Store({
    state: {},
    getters: {
    getVacation: state => {
        return state.vacation
    },
    guests: state => {
        return state.vacation.guests
    },
    verifiedGuests: state => {
        return state.vacation.guests.filter(guest => guest.verified)
    },
    emergencyContacts: state => {
        return state.emergency_contacts
    },
    arrival: state => {
        return state.vacation.check_in
    },
    departure: state => {
        return state.vacation.check_out
    },
    countdown : state => {
        let check_in = new Date(state.vacation.check_in);
        let now = new Date();

        if ((now - check_in) > 0) {
            return 'This vacation started on ' + check_in;
        }

        let difference = check_in - now;
        let day = 1000 * 60 * 60 * 24;

        return Math.ceil(difference / day) + " days until your vacation";
    }
},
mutations: {
    [UPDATE_VACATION] (state, payload) {
        state.vacation = payload.vacation;
    },
    [ADD_GUEST] (state, payload) {
        state.vacation.guests.Push(payload.guest);
    },
    [REMOVE_GUEST] (state, payload){
        state.vacation.guests.filter(guest => { debugger; return guest.id != payload.guest.id})
    },
    [UPDATE_GUEST] (state, payload){
        state.vacation.guests.map(guest => {
            // Refactor Object.assign to deep cloning of object
            return guest.id === payload.guest.id ? Object.assign({}, guest, payload.guest) : guest;
        })
    },
    [ADD_EMERGENCY] (state, payload){
        state.vacation.emergency_contacts.Push(payload.emergency_contact)
    },
    [REMOVE_EMERGENCY] (state, payload){
        state.vacation.emergency_contacts.filter(contact => contact.id !== payload.emergency_contact.id)
    },
    [UPDATE_EMERGENCY] (state, payload){
        state.vacation.emergency_contacts.map(contact => {
            // Refactor not needed because emergency_contact is a shallow object.
           return contact.id === payload.emergency_contact.id ? Object.assign({}, contact, payload.emergency_contact) : contact;
        });
    }
},
actions: {
    getVacation({commit}){
      api.getVacation().then(vacation => commit(UPDATE_VACATION, vacation))
    },
    addGuest({commit}, guest){
        commit(ADD_GUEST, guest);
    },
    removeGuest({commit}, guest){
        commit(REMOVE_GUEST, guest);
    },
    updateGuest({commit}, guest){
        commit(UPDATE_GUEST, guest);
    },
    addEmergency({commit}, guest){
        commit(ADD_EMERGENCY, contact)
    },
    removeEmergency({commit}, contact){
        commit(REMOVE_EMERGENCY, contact)
    },
    updateEmergency({commit}, contact){
        commit(UPDATE_EMERGENCY, contact)
    },
    updateServer(store, payload){
      return api.saveVacation(payload)
    }
}

});

そのため、ソリューションは他の人に明確です:

店舗自体で初期状態を適切に設定していませんでした。データを取得し、ストアを正しく更新していましたが、ストアを次のように初期化する必要がありました。

export default new Vuex.Store({
 state: {
     vacation: {}//I added this, and then justed updated this object on create of the root Vue Instance
 },
});
41
calbear47

あなたはすべてを正しくやっていると思います。たぶん、ゲッターを正しく作成していないだけかもしれません(コードに定義が表示されません)。または、初期状態が正しく設定されていません(スニペットにも表示されません)。

mapStateを使用して、コンポーネントで状態プロパティを使用できるようにします。

デモでは、usersメソッドパラメータの配列にmapStateを追加するだけで、コンポーネントでユーザーデータが利用可能になります。 (ゲッターusersを追加して、これがどのように機能するかを示しました。mapStateを使用している場合は必要ありません。)

以下のデモまたはこれをご覧ください fiddle

const api =
  'https://jsonplaceholder.typicode.com/users'

const UPDATE_USERS = 'UPDATE_USERS'
const SET_LOADING = 'SET_LOADING'

const store = new Vuex.Store({
  state: {
    users: {},
    loading: false
  },
  mutations: {
    [UPDATE_USERS](state, users) {
      console.log('mutate users', users)
      state.users = users;
      console.log(state)
    }, [SET_LOADING](state, loading) {
      state.loading = loading;
    }
  },
  getters: {
    users(state) {
      return state.users
    }
  },
  actions: {
    getUsers({commit}) {
      commit(SET_LOADING, true);
      return fetchJsonp(api)
        .then((users) => users.json())
        .then((usersParsed) => {
          commit(UPDATE_USERS, usersParsed)
          commit(SET_LOADING, false)
        })
    }
  }
})

const mapState = Vuex.mapState;

const Users = {
  template: '<div><ul><li v-for="user in users">{{user.name}}</li></ul></div>',
  computed: mapState(['users'])
}

new Vue({
  el: '#app',
  store: store,
  computed: {
    ...mapState(['loading']),
      //...mapState(['users']),
      /*users () { // same as mapState
        return this.$store.state.users;
      }*/
      users() { // also possible with mapGetters(['users'])
        return this.$store.getters.users
      }
  },
  created() {
    this.$store.dispatch('getUsers')
  },
  components: {
    Users
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch-jsonp/1.0.5/fetch-jsonp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.1.1/vuex.min.js"></script>
<div id="app">
  <div v-if="loading">loading...</div>
  <users></users>
  <pre v-if="!loading">{{users}}</pre>
</div>
23
AWolf