Search code examples
javascriptvue.jsvuejs2vuexvuex-modules

Vuex update view on commit


I'm new on Vuex js and everything about stores.

I've got the following store.js

import Vuex from 'vuex'
import Vue from 'vue'
import axios from 'axios';


Vue.use(Vuex)

export const store = new Vuex.Store({
        state: {
            pacientes: []
        },
        mutations: {
            getPacientes() {
                axios.get('http://slimapi/api/pacientes')
                .then(response => {
                    this.state.pacientes = response.data
                })
                .catch(e => {
                    console.log(e)
                })  
                }
        }

});

Then i do a commit when i click a button on a modal (v-dialog) calling this function

 agregarTurno()
  {
    axios.post('/api/pacientes/add', {

        ...

     }).then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });

               this.$store.dispatch('fetchPacientes')
  }

And then i've my turno.vue (take it as app.vue) where i retrieve the state of the store. However when the commit is made, the state it's not updating on the view!.

I'm trying to update the view when the state is changed!

turno.vue

export default {
  name: 'turno',
  data () {
    return {
      pacientes: this.$store.state
      }
    },
    created() {
      this.$store.commit('getPacientes')
    },
    mounted() {
      this.$store.commit('getPacientes')
    },
  components: {
    tarjetaTurno,
    bottomNav,
    modalTurno 
  }
}

I've been ready some posts, some of them talk about using computed property but i cant understand how to and if it's what i need to update the view everytime the state change.

Thank you everyone!



EDIT SOLVED

store.js

export const store = new Vuex.Store({
        state: {
            pacientes: []
        },
        mutations: {
            setPacientes(state, payload) {
                state.pacientes = payload.pacientes
            }
        },
        actions: {
            fetchPacientes({commit}) {
                axios.get('http://slimapi/api/pacientes')
                     .then(response => {
                        commit('setPacientes', {pacientes: response.data}); //After getting patients, set the state
                     })
                     .catch(e => {
                        console.log(e)
                     }) 
            },
            addPacientes(context){
                axios.post('/api/pacientes/add', {
                    nombre: "test",
                    apellido: "test",
                    edad: "test",
                    peso_inicial:"test",
                    patologia:"test"
                   }).then(function (response){
                        context.dispatch('fetchPacientes'); //After adding patient, dispatch the fetchPatient to get them and set state 
                    })
                    .catch(function (error) {
                      console.log(error);
                    });          
            }
        }
});

Component modal which call the function to add patient:

  agregarTurno()
  {
    this.$store.dispatch('addPacientes');
  }

Turno.vue (root) who update when state changes

export default {
  name: 'turno',
  data () {
    return {
      pacientes: this.$store.state
      }
    },
    created() {
      this.$store.dispatch('fetchPacientes')
    },

Solution

  • Your problem is that mutations are synchronous, while you're making an axios.get call inside your mutation (which is asynchronous).

    You should handle the axios.get either in your component or in a vuex action:

    Using a vuex action to handle the axios.get() call:

    export const store = new Vuex.Store({
      state: {
        pacientes: []
      },
      mutations: {
        setPacientes(state, payload) {
          // note how we don't call this.state.
          // state comes as param.
          state.pacientes = payload.pacientes;
        },
      actions: {
        fetchPacientes(context){
          axios.get('http://slimapi/api/pacientes')
          .then(response => {
              context.commit('setPacientes', {pacientes: response.data});
          })
          .catch(e => {
              console.log(e)
          }) 
        }
      }
      }
    });
    

    And then in your component:

    agregarTurno() {
      // commit is for mutations, while dispatch is for actions
      this.$store.dispatch('fetchPacientes')
    }
    

    avoiding vuex action:

    You could make the api call in your component, and then just commit the mutation:

    agregarTurno() {
      axios.get('http://slimapi/api/pacientes')
      .then(response => {
        this.$store.commit('setPacientes', {pacientes: response.data});
      })
      .catch(e => {
          console.log(e)
      })
    }
    

    However, if several components will make this api call, you can avoid code duplication by placing it in a vuex action.

    I think that placing that kind of async code in the store as a vuex action is generally considered a best practice.

    Some issues in your pasted code

    You don't need to dispatch an action both in created() and mounted() hooks. I guess created() is the best option.

    To make your component react to the store, setup a data() member that refers to the pacientes in the store is ok, I would do:

    data () {
      return {
        pacientes: this.$store.state.pacientes
        }
      },
    

    Now, if your component would use a lot of things of the store, I would setup a vuex getter.