从 Vuex 操作返回 Promise

Returning Promises from Vuex actions

我最近开始将一些东西从 jQ 迁移到一个更结构化的框架 VueJS,我喜欢它!

从概念上讲,Vuex 对我来说有点范式转变,但我相信我现在知道它的全部内容,并且完全理解它!但是存在一些小的灰色区域,主要是从实现的角度来看。

这个我感觉设计的不错,不知道和Vuex的单向数据流cycle是否矛盾

基本上,从操作中 return promise(-like)对象被认为是好的做法吗?我将它们视为异步包装器,具有失败状态等,因此看起来很适合 return 承诺。相反,突变体只是改变事物,并且是 store/module.

中的纯结构

actions 在 Vuex 中是异步的。让调用函数(动作的发起者)知道动作已完成的唯一方法是 returning 一个 Promise 并稍后解析它。

这是一个示例:myAction returns a 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 到 return 和 Promise 非常有益。否则,操作发起者无法知道发生了什么,也无法知道什么时候事情足够稳定,可以在用户界面上显示一些东西。

关于 mutators 的最后一点说明 - 正如您正确指出的那样,它们是同步的。它们更改 state 中的内容,通常从 actions 中调用。无需将 Promisesmutators 混合,因为 actions 处理该部分。

编辑:我对单向数据流的Vuex循环的看法:

如果您在组件中访问 this.$store.state["your data key"] 之类的数据,则数据流是单向的。

action 的 promise 只是让组件知道 action 已经完成。

组件可以从上例中的 promise resolve 函数中获取数据(不是单向的,因此不推荐),或者直接从 $store.state["your data key"] 中获取数据,这是单向的并且遵循 vuex 数据生命周期。

以上段落假定您的修改器使用 Vue.set(state, "your data key", http_data),一旦您的操作完成了 http 调用。

仅针对封闭主题的信息: 你不必创建一个承诺,axios returns 一个本身:

参考: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'
        }
    },

操作数

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

TL:DR; return 承诺您仅在必要时采取行动,但 DRY 链接相同的行动。

很长一段时间以来,我也认为 returning 操作与 uni-directional 数据流的 Vuex 循环相矛盾。

但是,在边缘案例中,return从你的行为中得到的承诺可能是"necessary"。

想象这样一种情况,其中可以从 2 个不同的组件触发一个操作,并且每个组件以不同的方式处理故障情况。 在那种情况下,需要将调用者组件作为参数传递以在商店中设置不同的标志。

哑示例

用户可以在导航栏和 /profile 页面(包含导航栏)中编辑用户名的页面。两者都触发一个动作 "change username",这是异步的。 如果承诺失败,页面应该只在用户试图更改用户名的组件中显示错误。

当然这是一个愚蠢的例子,但我没有找到解决这个问题的方法,而不是复制代码并在 2 个不同的操作中进行相同的调用。

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>