Vuex Action Enhancer Example

by ktsn

HTML

<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
  <button type="button" @click="fetchMessage">Fetch Message</button>
  <p class="loading" v-show="loading">Loading...</p>
  <p class="error" v-if="error">{{ error }}</p>
  <ul class="messages">
    <li v-for="m in messages">{{ m }}</li>
  </ul>
</div>

JavaScript

function loadingEnhancer(action) {
  return function enhancedAction(ctx, payload) {
    ctx.commit('loading', payload)
    return action(ctx, payload)
    	.then(
        result => ctx.commit('success', {
          result,
          payload
        }),
        error => ctx.commit('error', {
        	error,
          payload
        })
      )
  }
}

// Mock ajax request to get a message
// It may fails with a error message
function getMessage() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() < 0.3) {
        reject('Failed to fetch a message')
      } else {
        resolve('Random Number: ' + Math.floor(Math.random() * 100))
      }
    }, 1000)
  })
}

const store = new Vuex.Store({
  state: {
    loading: false,
    error: null,
    messages: []
  },
  
  mutations: {
    loading (state) {
      state.loading = true
      state.error = null
    },
    
    success (state, { result }) {
    	state.loading = false
    },
    
    error (state, { error }) {
      state.loading = false
      state.error = error
    },
    
    addMessage (state, { message }) {
    	state.messages.push(message)
    }
  },
  
  actions: {
    fetchMessage: loadingEnhancer((ctx, payload) => {
      return getMessage().then(result => {
        ctx.commit('addMessage', { message: result })
      })
    })
  }
})

new Vue({
	el: '#app',
  store,
  computed: Vuex.mapState([
  	'loading',
    'error',
    'messages'
  ]),
  methods: Vuex.mapActions([
  	'fetchMessage'
  ])
})