VueJS: Action

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Các action tương tự như mutation, sự khác biệt là:

  • Thay vì biến đổi state, action thực hiện các mutation.
  • Action có thể chứa các hoạt động không đồng bộ tùy ý.

Hãy đăng ký một action đơn giản:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Xử lý action nhận được một đối tượng context mà bộc lộ cùng một tập hợp các phương thức/thuộc tính trên đối tượng store, vì vậy bạn có thể gọi context.commit để thực hiện một mutation, hoặc truy cập state và getters thông qua context.state và context.getters. Ta thậm chí có thể gọi các hành động khác với context.dispatch. Ta sẽ thấy lý do tại sao đối tượng ngữ cảnh này không phải là bản thân store khi ta giới thiệu các Mô-đun sau này.

Trong thực tế, ta thường sử dụng phép phân tích đối số ES2015 để đơn giản hóa mã một chút (đặc biệt khi chúng ta cần phải gọi commit nhiều lần):

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

#Gửi action

Các action được kích hoạt với phương thức store.dispatch:

store.dispatch('increment')

Điều này có vẻ như dump ngay từ đầu: nếu ta muốn tăng số lượng, tại sao ta không gọi store.commit('increment') trực tiếp? Hãy nhớ rằng các mutation phải đồng bộ? Action không. Ta có thể thực hiện các hoạt động không đồng bộ bên trong một action:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

Các action hỗ trợ định dạng tải trọng tương tự và gửi kiểu đối tượng:

// gửi với một payload
store.dispatch('incrementAsync', {
  amount: 10
})

// gửi với một object
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

Một ví dụ thực tế hơn về action trong thế giới thực sẽ là một action để thanh toán giỏ hàng, bao gồm việc gọi một API không đồng bộ và thực hiện nhiều mutation:

actions: {
  checkout ({ commit, state }, products) {
    // save the items currently in the cart
    const savedCartItems = [...state.cart.added]
    // send out checkout request, and optimistically
    // clear the cart
    commit(types.CHECKOUT_REQUEST)
    // the shop API accepts a success callback and a failure callback
    shop.buyProducts(
      products,
      // handle success
      () => commit(types.CHECKOUT_SUCCESS),
      // handle failure
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

Lưu ý rằng chúng tôi đang thực hiện một luồng các action không đồng bộ và ghi lại các tác dụng phụ (state mutation) của action bằng cách cam kết chúng.

#Gửi action trong các component

Bạn có thể gửi các action trong các component có this.$store.dispatch('xxx') hoặc sử dụng helper mapActions để map các phương thức component tới các lời gọi store.dispatch (yêu cầu store đưa vào root):

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // map `this.increment()` thành `this.$store.dispatch('increment')`

      // `mapActions` cũng hỗ trợ payload:
      'incrementBy' // map `this.incrementBy(amount)` thành `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
      add: 'increment' // map `this.add()` thành `this.$store.dispatch('increment')`
    })
  }
}

Soạn action

Các action thường không đồng bộ, vậy làm thế nào để chúng ta biết khi nào một action được thực hiện? Và quan trọng hơn, làm cách nào chúng ta có thể cùng nhau tạo nhiều action để xử lý các luồng không đồng bộ phức tạp hơn?

Điều đầu tiên cần biết là store.dispatch có thể xử lý Promise được trả về bởi trình xử lý action được kích hoạt và nó cũng trả về Promise:

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

Bây giờ bạn có thể làm:

store.dispatch('actionA').then(() => {
  // ...
})

Và cũng trong một action khác:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

Cuối cùng, nếu chúng ta sử dụng async/await , chúng ta có thể soạn các action của chúng ta như sau:

// giả sử `getData()` và `getOtherData()` trả về các Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for `actionA` to finish
    commit('gotOtherData', await getOtherData())
  }
}

Có thể store.dispatch kích hoạt nhiều trình xử lý action trong các mô-đun khác nhau. Trong trường hợp này, giá trị trả về sẽ là một Promise giải quyết khi tất cả các trình xử lý được kích hoạt đã được giải quyết.

» Tiếp: Module
« Trước: Mutation
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!