Front-end technology: how to execute methods in the UI after vuex state management action asynchronous call ends

1. Origin of the problem

Recently, when working on the vue.js project, we encountered the problem of communication and interoperability between vuex state management action and vue.js method.The scene is as follows:

2. First Solution

For example, when we initialize a page, we need to get data from the server through the API interface. Before the data is successfully obtained, we need to show the Loading status box. After the data is obtained, we need to hide the Loading status box.

This is a relatively simple application scenario, and of course the solution is simpler.

We can do this with the state data field, which stores a loading field in the state and sets the default value to false.

const store = new Vuex.Store({
  state: {
    loading: false
  },
  // ......
});

Since you are going to operate on state.loading, you need to define a mutation method to update the loading state data.

const UPDATE_LOADING = 'updateLoading';

const store = new Vuex.Store({
  // ......,
  mutations: {
    updateLoading (state, loading) {
      state.loading = loading;
    }
  },
  // ......
});

Then, we declare an action method for getting data from the HTTP API.

const store = new Vuex.Store({
  // ......,
  actions: {
    fetchData ({ commit }) {
      commit(UPDATE_LOADING, true);
      axios.get('...', { params: {...} })
        .then(res => {
          // TODO parses HTTP response data for related business logic processing
        })
        .catch(err => {
          // TODO for related error and exception handling
        })
        .finally(() => {
          commit(UPDATE_LOADING, false);
        });
    }
  },
  // ......
})

In the page template, we map the action method of vuex to the method of object in vue.js through the mapActions function.

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'fetchData',
      // ...
    ])
  }
}

Finally, the mounted lifecycle method of vue.js calls the method fetchData through the mapActions image.

export default {
  // ...,
  mounted() {
    this.fetchData();
  },
  // ...
}

3. Second Solution

The first solution described above is to use mutation to modify the state data of the state to control data rendering on the UI.However, if you want to transfer the resulting data to the UI component, it is not possible to do something in the UI after calling the action method to execute.

Previously, we knew that asynchronous methods pass data through the parameters of callback functions, so the second solution I mentioned was through callback functions.

const store = new Vuex.Store({
  // ......,
  actions: {
    fetchData ({ commit }, { params, callback }) {
      commit(UPDATE_LOADING, true);
      axios.get('...', { params })
        .then(res => {
          callback(res);
        })
        .catch(err => {
          // TODO for related error and exception handling
        })
        .finally(() => {
          commit(UPDATE_LOADING, false);
        });
    }
  },
  // ......
})

4. Third Solution

Although the above two methods can solve some problems, the solution is not elegant enough, and the first method has great limitations.For example, you cannot call back methods in the main interface to perform subsequent operations or pass parameters freely.The second method uses callbacks to call methods or pass them around, but callback calls are synchronous and the code style is not good.So I would rather encourage you to use a third method, which is to return a Promise when an action is called, so that you can get the promise object in the main interface and perform subsequent tasks in a chain, or pass the result data of the action asynchronous task to the main UI.

const store = new Vuex.Store({
  // ......,
  actions: {
    fetchData ({ commit }, { params }) {
      commit(UPDATE_LOADING, true);
      return axios.get('...', { params })
        .then(res => {
          const { data } = res;
          return data;
        })
        .finally(() => {
          commit(UPDATE_LOADING, false);
        });
    }
  },
  // ......
})

In the main UI, we can do the following things.

export default {
  // ...,
  mounted() {
    this.fetchData({ id: 1 })
      .then(res => {
        // TODO performs subsequent tasks
      })
      .catch(err => {
        // TODO Handles Exceptions
      });
  },
  // ...
}

Tags: Javascript Vue axios

Posted on Thu, 07 Nov 2019 19:11:46 -0500 by snaack