5
votes

I'm just starting to learn Vuex here. Until now I've been storing shared data in a store.js file and importing store in every module but this is getting annoying and I'm worried about mutating state.

What I'm struggling with is how to import data from firebase using Vuex. From what I understand only actions can make async calls but only mutations can update the state?

Right now I'm making calls to firebase from my mutations object and it seems to be working fine. Honestly, all the context, commit, dispatch, etc. seems a bit overload. I'd like to just be able to use the minimal amount of Vuex necessary to be productive.

In the docs it looks like I can write some code that updates the state in the mutations object like below, import it into my component in the computed property and then just trigger a state update using store.commit('increment'). This seems like the minimum amount necessary to use Vuex but then where do actions come in? Confused :( Any help on the best way to do this or best practices would be appreciated!

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

My code is below

store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex); 

const db = firebase.database();
const auth = firebase.auth();

const store = new Vuex.Store({
  state: {
    userInfo: {},
    users: {},
    resources: [],
    postKey: ''
  },
  mutations: {

    // Get data from a firebase path & put in state object

    getResources: function (state) {
      var resourcesRef = db.ref('resources');
      resourcesRef.on('value', snapshot => {
          state.resources.push(snapshot.val());
      })
    },
    getUsers: function (state) {
      var usersRef = db.ref('users');
      usersRef.on('value', snapshot => {
          state.users = snapshot.val();
      })  
    },
    toggleSignIn: function (state) {
      if (!auth.currentUser) {
          console.log("Signing in...");
          var provider = new firebase.auth.GoogleAuthProvider();
          auth.signInWithPopup(provider).then( result => {
          // This gives you a Google Access Token. You can use it to access the Google API.
          var token = result.credential.accessToken;
          // The signed-in user info.
          var user = result.user;
          // Set a user
          var uid = user.uid;
          db.ref('users/' + user.uid).set({
              name: user.displayName,
              email: user.email,
              profilePicture : user.photoURL,
          });

          state.userInfo = user;
          // ...
          }).catch( error => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
          // ...
          });
      } else {
          console.log('Signing out...');
          auth.signOut();      
      }
    }
  }
})

export default store

main.js

import Vue from 'vue'
import App from './App'
import store from './store'

new Vue({
  el: '#app',
  store, // Inject store into all child components
  template: '<App/>',
  components: { App }
})

App.vue

<template>
  <div id="app">
    <button v-on:click="toggleSignIn">Click me</button>
  </div>
</template>

<script>

import Hello from './components/Hello'


export default {
  name: 'app',
  components: {
    Hello
  },
  created: function () {
    this.$store.commit('getResources'); // Trigger state change
    this.$store.commit('getUsers'); // Trigger state change
  },
  computed: {
    state () {
      return this.$store.state // Get Vuex state into my component
    }
  },
  methods: {
    toggleSignIn () {
      this.$store.commit('toggleSignIn'); // Trigger state change
    }
  }
}

</script>

<style>

</style>
1

1 Answers

19
votes

All AJAX should be going into actions instead of mutations. So the process would start by calling your action

...which commits data from the ajax callback to a mutation

...which is responsible for updating the vuex state.

Reference: http://vuex.vuejs.org/en/actions.html

Here is an example:

// vuex store

state: {
  savedData: null
},
mutations: {
  updateSavedData (state, data) {
    state.savedData = data
  }
},
actions: {
  fetchData ({ commit }) {
    this.$http({
      url: 'some-endpoint',
      method: 'GET'
    }).then(function (response) {
      commit('updateSavedData', response.data)
    }, function () {
      console.log('error')
    })
  }
}

Then to call your ajax, you will have to call the action now by doing this:

store.dispatch('fetchData')

In your case, just replace this.$http({...}).then(...) with your firebase ajax and call your action in the callback.