Trả lại lời hứa từ hành động của Vuex


130

Gần đây tôi đã bắt đầu chuyển mọi thứ từ jQ sang một khung có cấu trúc chặt chẽ hơn là VueJS, và tôi thích nó!

Về mặt khái niệm, Vuex là một sự thay đổi mô hình đối với tôi, nhưng tôi tự tin rằng tôi biết tất cả những gì về nó bây giờ, và hoàn toàn hiểu được nó! Nhưng tồn tại một vài khu vực màu xám nhỏ, chủ yếu là từ quan điểm thực hiện.

Thiết kế này tôi cảm thấy tốt bởi thiết kế, nhưng không biết liệu nó có mâu thuẫn với chu trình Vuex của luồng dữ liệu không định hướng hay không.

Về cơ bản, nó có được coi là thực hành tốt để trả lại một đối tượng hứa hẹn (giống như) từ một hành động không? Tôi coi những thứ này là các hàm bao không đồng bộ, với các trạng thái thất bại và tương tự, vì vậy có vẻ như là một cách phù hợp để trả lại một lời hứa. Ngược lại, các trình biến đổi chỉ thay đổi mọi thứ và là các cấu trúc thuần túy trong một cửa hàng / mô-đun.

Câu trả lời:


254

actionstrong Vuex là không đồng bộ. Cách duy nhất để cho chức năng gọi (người khởi xướng hành động) biết rằng một hành động đã hoàn tất - là trả lại một Lời hứa và giải quyết nó sau.

Dưới đây là một ví dụ: myActiontrả về a Promise, thực hiện cuộc gọi http và giải quyết hoặc từ chối cuộc gọi Promisesau - tất cả không đồng bộ

actions: {
    myAction(context, data) {
        return new Promise((resolve, reject) => {
            // Do something here... lets say, a http call using vue-resource
            this.$http("/api/something").then(response => {
                // http success, call the mutator and change something in state
                resolve(response);  // Let the calling function know that http is done. You may send some data back
            }, error => {
                // http failed, let the calling function know that action did not work out
                reject(error);
            })
        })
    }
}

Bây giờ, khi thành phần Vue của bạn bắt đầu myAction, nó sẽ nhận được đối tượng Promise này và có thể biết liệu nó có thành công hay không. Dưới đây là một số mã mẫu cho thành phần Vue:

export default {
    mounted: function() {
        // This component just got created. Lets fetch some data here using an action
        this.$store.dispatch("myAction").then(response => {
            console.log("Got some data, now lets show something in this component")
        }, error => {
            console.error("Got nothing from server. Prompt user to check internet connection and try again")
        })
    }
}

Như bạn có thể thấy ở trên, rất có lợi cho actionsviệc trả lại a Promise. Mặt khác, không có cách nào để người khởi xướng hành động biết điều gì đang xảy ra và khi mọi thứ đủ ổn định để hiển thị một cái gì đó trên giao diện người dùng.

Và một lưu ý cuối cùng liên quan mutators- như bạn đã chỉ ra, chúng là đồng bộ. Họ thay đổi công cụ trong state, và thường được gọi từ actions. Không cần phải trộn Promisesvới mutators, như phần actionsxử lý phần đó.

Chỉnh sửa: Quan điểm của tôi về chu trình Vuex của luồng dữ liệu đơn hướng:

Nếu bạn truy cập dữ liệu như this.$store.state["your data key"]trong các thành phần của mình, thì luồng dữ liệu là một hướng.

Lời hứa từ hành động chỉ để cho thành phần biết rằng hành động đó đã hoàn tất.

Thành phần có thể lấy dữ liệu từ chức năng giải quyết lời hứa trong ví dụ trên (không phải là một hướng, do đó không được đề xuất) hoặc trực tiếp từ $store.state["your data key"]đó là đơn hướng và tuân theo vòng đời dữ liệu vuex.

Đoạn trên giả định rằng trình biến đổi của bạn sử dụng Vue.set(state, "your data key", http_data), sau khi cuộc gọi http được hoàn thành trong hành động của bạn.


4
"Như bạn có thể thấy ở trên, rất có lợi cho các hành động trả lại Lời hứa. Nếu không, không có cách nào để người khởi xướng hành động biết điều gì đang xảy ra và khi mọi thứ đủ ổn định để hiển thị một cái gì đó trên giao diện người dùng." IMO, điều này là thiếu điểm của Vuex. Người khởi xướng hành động không cần biết chuyện gì đang xảy ra. Hành động sẽ làm thay đổi trạng thái khi dữ liệu quay trở lại từ sự kiện không đồng bộ và thành phần sẽ phản ứng với thay đổi giai đoạn đó dựa trên trạng thái của cửa hàng Vuex, không phải là Lời hứa.
ceejayoz

1
@ceejayoz Đồng ý, nhà nước nên là nguồn sự thật duy nhất cho tất cả các đối tượng dữ liệu. Nhưng Lời hứa là cách duy nhất để liên lạc lại với người khởi xướng hành động. Ví dụ: nếu bạn muốn hiển thị nút "Thử lại" sau khi http không thành công, thông tin đó không thể chuyển sang trạng thái, nhưng chỉ có thể được truyền lại qua a Promise.reject().
Mani

1
Điều đó có thể dễ dàng được xử lý trong cửa hàng Vuex. Bản thân hành động có thể bắn một bộ failedbiến đổi thiết lập state.foo.failed = true, mà thành phần có thể xử lý. Không cần lời hứa được chuyển đến thành phần cho điều đó, và như một phần thưởng, bất cứ điều gì khác muốn phản ứng với cùng một thất bại cũng có thể được thực hiện từ cửa hàng.
ceejayoz

4
@ceejayoz Kiểm tra các hành động soạn thảo (phần cuối) trong các tài liệu - vuex.vuejs.org/en/ilities.html - các hành động không đồng bộ và do đó trả lại một Lời hứa là một ý tưởng hay, như đã nêu trong các tài liệu đó. Có thể không phải trong trường hợp $ http ở trên, nhưng trong một số trường hợp khác, chúng ta có thể cần biết khi nào một hành động được hoàn thành.
Mani

6
@DanielPark Có, "nó phụ thuộc" vào kịch bản và tùy chọn nhà phát triển cá nhân. Trong trường hợp của tôi, tôi muốn tránh các giá trị trung gian như {isLoading:true}trong trạng thái của mình và do đó đã dùng đến Lời hứa. Sở thích của bạn có thể thay đổi. Vào cuối ngày, mục tiêu của chúng tôi là viết mã không lộn xộn và có thể bảo trì. Liệu lời hứa có đạt được mục tiêu đó hay trạng thái vuex - được để cho các nhà phát triển và nhóm riêng lẻ quyết định.
Mani

41

Chỉ cần một thông tin về một chủ đề kín: bạn không cần phải tạo lời hứa, các axios sẽ tự trả về:

Tham chiếu: https://forum.vuejs.org/t/how-to-resolve-a-promise-object-in-a-vuex-action-and-redirect-to-another-route/18254/4

Thí dụ:

    export const loginForm = ({ commit }, data) => {
      return axios
        .post('http://localhost:8000/api/login', data)
        .then((response) => {
          commit('logUserIn', response.data);
        })
        .catch((error) => {
          commit('unAuthorisedUser', { error:error.response.data });
        })
    }

Một vi dụ khac:

    addEmployee({ commit, state }) {       
      return insertEmployee(state.employee)
        .then(result => {
          commit('setEmployee', result.data);
          return result.data; // resolve 
        })
        .catch(err => {           
          throw err.response.data; // reject
        })
    }

Một ví dụ khác với async-await

    async getUser({ commit }) {
        try {
            const currentUser = await axios.get('/user/current')
            commit('setUser', currentUser)
            return currentUser
        } catch (err) {
            commit('setUser', null)
            throw 'Unable to fetch current user'
        }
    },

Ví dụ cuối cùng không nên thừa vì các hành động của axios được mặc định là không đồng bộ?
nonN NumicalFloat

9

Hành động

ADD_PRODUCT : (context,product) => {
  return Axios.post(uri, product).then((response) => {
    if (response.status === 'success') {  
      context.commit('SET_PRODUCT',response.data.data)
    }
    return response.data
  });
});

Thành phần

this.$store.dispatch('ADD_PRODUCT',data).then((res) => {
  if (res.status === 'success') {
    // write your success actions here....
  } else {
     // write your error actions here...
  }
})

2
phản hồi không hoạt động này không được xác định trong thành phần
Nand Lal

1
Tôi nghĩ rằng bạn đã quên thêm trả lại trong hàm ADD_PRODVEL
Bhaskararao Gummidi

Nên viết thường chữ viết hoa trên mạng.
bigp

Tôi lấy Axois là const đang nhập từ 'axios'
Bhaskararao Gummidi

0

TL: DR; trả lại lời hứa từ hành động của bạn chỉ khi cần thiết, nhưng DRY xâu chuỗi các hành động tương tự.

Trong một thời gian dài tôi cũng mặc dù các hành động trở lại đó mâu thuẫn với chu trình Vuex của luồng dữ liệu không định hướng.

Nhưng, có những TRƯỜNG HỢP EDGE trong đó việc trả lại lời hứa từ hành động của bạn có thể là "cần thiết".

Tưởng tượng một tình huống trong đó một hành động có thể được kích hoạt từ 2 thành phần khác nhau và mỗi thành phần xử lý trường hợp thất bại khác nhau. Trong trường hợp đó, người ta sẽ cần phải vượt qua thành phần người gọi làm tham số để đặt các cờ khác nhau trong cửa hàng.

Ví dụ ngu ngốc

Trang nơi người dùng có thể chỉnh sửa tên người dùng trong thanh điều hướng và trang / hồ sơ (có chứa thanh điều hướng). Cả hai đều kích hoạt một hành động "thay đổi tên người dùng", không đồng bộ. Nếu thất hứa, trang sẽ chỉ hiển thị lỗi trong thành phần mà người dùng đang cố thay đổi tên người dùng.

Tất nhiên đó là một ví dụ ngớ ngẩn, nhưng tôi không thấy cách nào để giải quyết vấn đề này mà không cần sao chép mã và thực hiện cùng một cuộc gọi trong 2 hành động khác nhau.


-1

Action.js

const axios = require('axios');
const types = require('./types');

export const actions = {
  GET_CONTENT({commit}){
    axios.get(`${URL}`)
      .then(doc =>{
        const content = doc.data;
        commit(types.SET_CONTENT , content);
        setTimeout(() =>{
          commit(types.IS_LOADING , false);
        } , 1000);
      }).catch(err =>{
        console.log(err);
    });
  },
}

nhà.vue

<script>
  import {value , onCreated} from "vue-function-api";
  import {useState, useStore} from "@u3u/vue-hooks";

  export default {
    name: 'home',

    setup(){
      const store = useStore();
      const state = {
        ...useState(["content" , "isLoading"])
      };
      onCreated(() =>{
        store.value.dispatch("GET_CONTENT" );
      });

      return{
        ...state,
      }
    }
  };
</script>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.