Correct way to cache data in vuex

11,562

Solution 1

There is a correct way to do what you want but it is not the way you are doing it. Vue is quite strict on "Do not mutate vuex store state outside mutation handlers."

This means you should only alter the store state through a mutation, then use your getter only to get the data. You should also use an action to commit the mutation. So for what you are trying to do you should try it like this.

// AnyPage.vue

<template>
 <div>{{myData}}</div>
</template>

<script>
export default {
  data() {
    return {
      myData:null
    }
  },
  async created(){
    if(this.$store.state._myData === null) {
      await this.$store.dispatch('getData')
    }
    this.myData = this.$store.getters.myData;
  }
}
</script>

then in your store:

// store.js

export default new Vuex.Store({
  state: {
    _myData: null,
  },
  getters: {
    myData: (state) => state._myData,
  }
  mutations: {
    SET_DATA(state, data) {
      state._myData = data
    }
  }
  actions: {
    getData({ context }){
        axios.get('http://myData.com/')
        .then(res => {
          context.commit('SET_DATA', res)
        })
      }
    }
  }
});

You should read up in the docs which covers it all pretty well.

Solution 2

Action handlers receive a context object which exposes the same set of methods/properties on the store instance, so you can call context.commit to commit a mutation, or access the state and getters via context.state and context.getters. https://vuex.vuejs.org/guide/actions.html

try this:

// AnyPage.vue

<template>
 <div>{{myData}}</div>
</template>

<script>
export default {
  computed: {
    myData () {
      return this.$store.state.myData
    }
  },
  mounted () {
    this.$store.dispatch('getData')
  }
}
</script>

in store file:

// store.js

export default new Vuex.Store({
  state: {
    myData: null,
  },
  mutations: {
    SET_DATA(state, data) {
      state.myData = data
    }
  }
  actions: {
    getData({ context }){
        if (context.state.myData === null) {
            axios.get('http://myData.com/')
            .then(res => {
                context.commit('SET_DATA', res)
            })
        }
      }
    }
  }
});
Share:
11,562
als9xd
Author by

als9xd

Updated on June 08, 2022

Comments

  • als9xd
    als9xd almost 2 years

    I am trying to asynchronously load data into vuex that is static but is used by multiple routes.I only want to fetch the data once and only when a route that needs it is visited. This is what I'm currently doing but I'm not sure if this is the correct convention or if there is a better/more Vueish way.

    // store.js
    
    export default new Vuex.Store({
      state: {
        _myData: null,
      },
      getters: {
        myData: (state) => new Promise((resolve,reject) => {
          if(state._myData){
            resolve(state._myData);
          } else {
            axios.get('http://myData.com/')
            .then(myData => {
              state._myData = myData;
              resolve(state._myData);
            });
          }
        })
      }
    });
    
    // ProfilePage.vue
    
    <template>
     <div>{{myData}}</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          myData:null
        }
      },
      async created(){
       this.myData = await this.$store.getters.myData;
      }
    }
    </script>
    
    // AboutPage.vue
    
    <template>
     <div>{{myData}}</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          myData:null
        }
      },
      async created(){
       this.myData = await this.$store.getters.myData;
      }
    }
    </script>
    
  • als9xd
    als9xd about 5 years
    Is there any reason for having a getter in your example? Couldn't I just access the state variable directly?
  • Andrew1325
    Andrew1325 about 5 years
    @als9xd the example above doesn't essentially need one but using getters is vue best practice and is essential if you need to compute the store state, for instance running a filter function. It's a good habit to have.
  • Zim
    Zim over 4 years
    getters are ideal when multiple components need the same data codeburst.io/…
  • LittleTiger
    LittleTiger over 4 years
    How is this answer useful? Seems it was written after the other Andrew's answer and you're essentially saying exactly the same? :-/
  • Marcelo Gondim
    Marcelo Gondim over 4 years
    hi, here i used computed property, wish make the variable reactive
  • Half_Duplex
    Half_Duplex almost 4 years
    This works when used with <div>{{myData}}</div>, but that is not very practical. It breaks in any real world use case, for example <div>{{myData.aProp}}</div>. At render, aProp is null and will result in console errors. What would be the best way to avoid that?
  • Andrew1325
    Andrew1325 almost 4 years
    Without knowing what your myData object contains I cannot say what this issue could be, but there is no reason you can't extract a property from it. If you are having a problem you could try making myData a computed property as the other answer shows although i would access it using mapGetters.