1

I use TypeScript with Vue/Vuex to create a toy app, in which I need to load a list of items from remote API upon rendering a component. In the actions below, I use the library axios to make http request, and I return it as a promise:

const actions = {    
  getCurrentSession(context: Context) {
    return axios.get("http://localhost:3000/sessions/current")
      .then((res) => {
        commitSharedFiles(context, res.data.shared_files);
      })
      .catch((e: string) => {
        console.error(e);
      });
  }
};

// export action
export const dispatchGetSharedFiles = dispatch(actions.getCurrentSession);

// commit
export const commitSharedFiles = commit(mutations.setSharedFileList);

// mutations to the state tree
const mutations = {
  setSharedFileList(state: State, sharedFiles: StoreState.DirFile[]) {
    state.sharedFileList = sharedFiles;
  }
};

Because of the asynchronous nature of the actions, I have to resolve the promise before retrieving the fetched list of files from the store/state tree:

// this is called in the mounted() life cycle method:
Store.dispatchGetSharedFiles(this.$store).then(res => {
            this.sharedFiles = Store.readSharedFilesList(this.$store);
});

This works, but I think it is very convoluted to resolve the promise and then get the data. Is there a better way to use async actions in Vuex? thanks

TonyW
  • 18,375
  • 42
  • 110
  • 183
  • Looks like you've implemented the [explicit promise construction anti-pattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). Don't do that – Phil Apr 16 '18 at 04:37
  • thanks i just removed the Promise wrapper :) – TonyW Apr 16 '18 at 04:42
  • What is `commit` in the context of your code snippet? Same for `dispatch`? The whole `commitSharedFiles` bit doesn't make much sense. You're calling it as a function but it doesn't appear to be one, unless `commit(...)` somehow returns a function – Phil Apr 16 '18 at 04:43
  • commit returns a function committing mutation directed to the specified mutation handler. `dispatch` dispatching action directed to the specified action handler. thanks – TonyW Apr 16 '18 at 05:12
  • Could you please show those implementations? Your question doesn't make much sense without them – Phil Apr 16 '18 at 05:55

1 Answers1

2

Use async/await for the action and a mapped getter for retrieving the items from the store (could map an action also).

// store.ts
const actions = {    
  async FetchSharedFiles(context: Context) {
    // omitted error handling for brevity
    let {res} = await axios.get("http://localhost:3000/sessions/current")
    commitSharedFiles(context, res.data.shared_files);
  }
};

// component.ts
import { mapGetters } from 'vuex'
...
mounted () {
  // again, do any necessary error handling
  this.$store.dispatch('FetchSharedFiles')
},
computed: {
  ...mapGetters({
    sharedFiles: 'namespace/getSharedFiles'
  })
}

Using this pattern benefits from vuex's reactivity so sharedFiles in your component would trigger an update when the action finishes and commits the response data. Your template then might look like:

<template>
  <div v-for="(file, i) in sharedFiles" :key="i">
    // layout for each item...
  </div>
</template>
Brian Lee
  • 17,904
  • 3
  • 41
  • 52