从Vuex操作返回承诺


130

最近,我开始将内容从jQ迁移到VueJS这样的结构化框架,我喜欢它!

从概念上讲,Vuex对我来说是一种范式转换,但是我有信心我知道现在的全部情况,并且完全可以理解!但是存在一些小的灰色区域,主要是从实现的角度来看。

我觉得这个设计很好,但是不知道它是否与单向数据流的Vuex 周期相矛盾。

基本上,从动作中返回一个promise(类似)对象是否被视为一种好习惯?我将它们视为异步包装程序,带有失败状态等,因此看起来很适合返回承诺。相反,变异器只是改变事物,是商店/模块中的纯结构。

Answers:


255

actions在Vuex中是异步的。让调用函数(动作的发起者)知道某个动作已完成的唯一方法是返回一个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,作为actions手柄部分。

编辑:我对单向数据流的Vuex周期的看法:

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

行动的承诺只是让组件知道行动已完成。

在上面的示例中,该组件可以从promise resolve函数获取数据(非单向,因此不建议使用),也可以直接从$store.state["your data key"]单向获取,并遵循vuex数据生命周期。

上面的段落假定您的mutator使用Vue.set(state, "your data key", http_data),一旦您的操作中完成http调用。


4
“如您在上面看到的,对于动作返回一个Promise很有好处。否则,动作发起者无法知道正在发生什么以及何时足够稳定以在用户界面上显示某些内容。” IMO,这遗漏了Vuex的要点。动作发起者不必知道发生了什么。当数据从异步事件返回时,该操作应更改状态,并且组件应基于Vuex存储的状态(而不是Promise)响应该阶段更改。
ceejayoz '16

1
@ceejayoz同意,状态应该是所有数据对象的唯一事实来源。但是,Promise是与动作发起者通信的唯一途径。例如,如果您想在http失败后显示“重试”按钮,则该信息无法进入状态,而只能通过进行回传Promise.reject()
玛尼

1
这可以在Vuex商店中轻松处理。动作本身可以触发一个failedset 的mutator,state.foo.failed = true组件可以处理。为此,无需将承诺传递给组件,而且,作为奖励,任何其他想对同一故障做出反应的其他事情也可以从商店中进行。
ceejayoz '16

4
@ceejayoz 在文档中查看组合动作(最后一节) -vuex.vuejs.org/en/actions.html-动作是异步的,因此返回Promise是一个好主意,如这些文档中所述。也许不是上面的$ http情况,但是在其他情况下,我们可能需要知道操作何时完成。
玛尼

6
@DanielPark是的,“取决于”方案和开发人员的个人偏好。就我而言,我想避免像{isLoading:true}我的州那样的中间价值观,因此诉诸于Promises。您的偏好可能会有所不同。归根结底,我们的目标是编写无混乱且可维护的代码。许诺是实现该目标还是vuex状态-留给各个开发人员和团队来决定。
玛尼

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
组件中未定义的无效响应
Nand Lal

1
我认为您忘了在ADD_PRODUCT函数中添加收益
Bhaskararao Gummidi 19'Aug

在“ axios”中应为小写字母“ a”。
bigp

我将Axois当作是从“ axios”导入的const
Bhaskararao Gummidi 19'Nov

0

TL:DR; 仅在必要时从您的操作中返回诺言,而是将相同的操作链接在一起。

很长一段时间以来,我还认为返回动作与单向数据流的Vuex周期相矛盾。

但是,也有边缘情况,其中从你的行动返回一个承诺可能是“必要的”。

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

哑巴的例子

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

当然,这是一个愚蠢的示例,但我看不到一种方法,无需复制代码并通过2个不同的动作进行相同的调用即可解决此问题。


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

<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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.