Vuex 작업에서 약속 반환


130

저는 최근에 jQ에서 VueJS 인보다 구조화 된 프레임 워크로 마이그레이션하기 시작했습니다.

개념적으로 Vuex는 저에게 약간의 패러다임 전환 이었지만 지금은 그 모든 것이 무엇인지 알고 있으며 완전히 이해합니다! 그러나 대부분 구현 관점에서 볼 때 약간의 회색 영역이 있습니다.

이것은 디자인 상 좋지만 단방향 데이터 흐름 의 Vuex 주기 와 모순되는지 모르겠습니다 .

기본적으로 액션에서 promise (유사) 객체를 반환하는 것이 좋은 방법으로 간주됩니까? 나는 이것을 실패 상태 등의 비동기 래퍼로 취급하므로 약속을 반환하기에 적합한 것처럼 보입니다. 반대로 뮤 테이터는 단지 사물을 변경하고 스토어 / 모듈 내의 순수한 구조입니다.

답변:


255

actionsVuex에서는 비동기식입니다. 호출 함수 (동작 개시 자)가 동작이 완료되었음을 알 수 있도록하는 유일한 방법은 Promise를 반환하고 나중에 해결하는 것입니다.

예를 들면 다음과 같습니다.를 myAction반환하고 Promise, http 호출을 수행하고, Promise나중을 확인 하거나 거부합니다. 모두 비동기 적으로

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);
            })
        })
    }
}

이제 Vue 구성 요소가를 시작 myAction하면이 Promise 개체를 가져와 성공 여부를 알 수 있습니다. 다음은 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")
        })
    }
}

위에서 볼 수 있듯이,을 위해 매우 유용합니다 actions를 반환 Promise. 그렇지 않으면 액션 개시자가 무슨 일이 일어나고 있는지 그리고 사용자 인터페이스에 무언가를 보여줄만큼 안정된시기를 알 수있는 방법이 없습니다.

그리고 마지막 메모 mutators-당신이 올바르게 지적했듯이 그들은 동 기적입니다. 그들은에서 항목을 변경 state하고 일반적으로에서 호출됩니다 actions. 혼합 할 필요가 없습니다 Promises으로 mutators는 AS, actions그 부분 핸들.

편집 : 단방향 데이터 흐름의 Vuex주기에 대한 내 견해 :

this.$store.state["your data key"]구성 요소에서 와 같이 데이터에 액세스하는 경우 데이터 흐름은 단방향입니다.

작업의 약속은 구성 요소에 작업이 완료되었음을 알리는 것입니다.

구성 요소는 위 예제의 약속 해결 기능에서 데이터를 가져 오거나 ( $store.state["your data key"]단방향이 아니므로 권장하지 않음) 직접 단방향이고 vuex 데이터 수명주기를 따릅니다.

위의 단락은 Vue.set(state, "your data key", http_data)http 호출이 작업에서 완료되면 mutator가를 사용한다고 가정합니다 .


4
"위에서 볼 수 있듯이, 액션이 Promise를 반환하는 것은 매우 유익합니다. 그렇지 않으면 액션 개시자가 무슨 일이 일어나고 있는지 그리고 사용자 인터페이스에 무언가를 보여줄만큼 안정된시기를 알 수있는 방법이 없습니다." IMO, 이것은 Vuex의 요점을 놓치고 있습니다. 액션 개시자는 무슨 일이 일어나고 있는지 알 필요 가 없습니다 . 작업은 데이터가 비동기 이벤트에서 돌아올 때 상태를 변경해야하며 구성 요소는 Promise가 아닌 Vuex 저장소의 상태를 기반으로 해당 단계 변경에 응답해야합니다.
ceejayoz

1
@ceejayoz 동의합니다. 상태는 모든 데이터 개체에 대한 단일 진실 소스 여야합니다. 그러나 Promise는 액션 개시 자와 다시 통신 할 수있는 유일한 방법입니다. 예를 들어 http 실패 후 "다시 시도"단추를 표시하려는 경우 해당 정보는 상태가 될 수 없지만 Promise.reject().
Mani

1
Vuex 스토어에서 쉽게 처리 할 수 ​​있습니다. 액션 자체는 구성 요소가 처리 할 수 failed있는를 설정 하는 뮤 테이터를 state.foo.failed = true실행할 수 있습니다. 이를 위해 컴포넌트에 약속을 전달할 필요가 없으며, 보너스로 동일한 실패에 대응하려는 다른 모든 것도 스토어에서이를 수행 할 수 있습니다.
ceejayoz

4
@ceejayoz 문서에서 작업 작성 (마지막 섹션) 확인 -vuex.vuejs.org/en/actions.html- 작업은 비동기이므로 해당 문서에 명시된대로 Promise를 반환하는 것이 좋습니다. 위의 $ http 경우가 아닐 수도 있지만 다른 경우에는 작업이 완료되는시기를 알아야 할 수도 있습니다.
Mani

6
@DanielPark 예, "그것은 시나리오와 개별 개발자의 선호도에 따라 다릅니다". 제 경우에는 {isLoading:true}제 상태 와 같은 중간 값을 피하고 싶었 기 때문에 약속에 의지했습니다. 귀하의 선호도는 다를 수 있습니다. 하루가 끝나면 우리의 목표는 깔끔하고 유지 가능한 코드를 작성하는 것입니다. 약속이 목표를 달성하는지 또는 vuex 상태를 달성하는지 여부는 개별 개발자와 팀이 결정하도록합니다.
Mani

41

닫힌 주제에 대한 정보를 위해 : 약속을 만들 필요가 없습니다. axios는 약속을 직접 반환합니다.

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

예:

    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 });
        })
    }

다른 예시:

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

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'
        }
    },

axios 작업이 기본적으로 이미 비동기식이므로 마지막 예제가 중복되지 않아야합니까?
nonNumericalFloat

9

행위

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
  });
});

구성 요소

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

2
이 작동하지 응답은 구성 요소에 정의되지 않은
낸드 랄에게

1
난 당신이 ADD_PRODUCT 함수에서 반환 추가하는 것을 잊었다 생각
Bhaskararao Gummidi을

"axios"에서 소문자 "a"여야합니다.
bigp

나는 'Axios의'에서 가져 오는 CONST로 Axois을 촬영
Bhaskararao Gummidi

0

TL : DR; 필요한 경우에만 작업에서 약속을 반환하지만 동일한 작업을 DRY 연결합니다.

오랜 시간 동안 나는 그 반환 작업이 단방향 데이터 흐름의 Vuex주기와 모순 되기는하지만.

하지만 EDGE CASES가 있습니다. 당신의 행동에서 약속을 돌려주는 것이 "필요한" 가 있습니다.

두 가지 다른 구성 요소에서 작업이 트리거 될 수 있고 각각 실패 사례를 다르게 처리하는 상황을 상상해보십시오. 이 경우 호출자 구성 요소를 매개 변수로 전달하여 상점에서 다른 플래그를 설정해야합니다.

멍청한 예

사용자가 navbar 및 / profile 페이지 (navbar 포함)에서 사용자 이름을 편집 할 수있는 페이지입니다. 둘 다 비동기식 인 "사용자 이름 변경"작업을 트리거합니다. 약속이 실패하면 페이지는 사용자가 사용자 이름을 변경하려는 구성 요소에만 오류를 표시해야합니다.

물론 멍청한 예이지만 코드를 복제하고 두 가지 다른 작업에서 동일한 호출을하지 않고는이 문제를 해결할 수있는 방법이 없습니다.


-1

actions.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);
    });
  },
}

home.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>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.