【问题标题】:Returning Promises from Vuex actions从 Vuex 操作返回 Promise
【发布时间】:2018-11-26 19:41:18
【问题描述】:

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

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

这个我觉得设计的不错,但是不知道是不是和vuex的cycle的单向数据流相矛盾。

基本上,从一个动作中返回一个 promise(-like) 对象被认为是一种好的做法吗?我将它们视为异步包装器,具有失败状态等,因此似乎很适合返回承诺。相反,mutators 只是改变事物,并且是存储/模块中的纯结构。

【问题讨论】:

    标签: promise vue.js es6-promise state-management vuex


    【解决方案1】:

    仅提供有关已关闭主题的信息: 你不必创建一个promise,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 操作在默认情况下已经是异步的吗?
    【解决方案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>
    

    【讨论】:

      【解决方案3】:

      TL:DR; 仅在必要时从您的操作返回承诺,但 DRY 链接相同的操作。

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

      但是,在边缘案例中,从你的行为中返回承诺可能是“必要的”。

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

      愚蠢的例子

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

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

      【讨论】:

        【解决方案4】:

        动作

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

        【讨论】:

        • 组件中未定义此无效响应
        • 我想你忘了在 ADD_PRODUCT 函数中添加返回
        • 在“axios”中应该是小写的“a”。
        • 我把 Axois 当作是从 'axios' 导入的常量
        【解决方案5】:

        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 调用。无需将Promisesmutators 混合,因为actions 处理该部分。

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

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

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

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

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

        【讨论】:

        • "正如你在上面看到的,返回 Promise 对 action 是非常有益的。否则 action 发起者无法知道发生了什么以及事情何时足够稳定以显示某些内容用户界面。” IMO,这是缺少 Vuex 的重点。动作发起者不应该需要知道发生了什么。当数据从异步事件返回时,该操作应该改变状态,并且组件应该根据 Vuex 存储的状态而不是 Promise 来响应该阶段的变化。
        • @ceejayoz 查看文档中的 组合动作(最后一节) - vuex.vuejs.org/en/actions.html - 动作是异步的,因此返回 Promise 是一个好主意,如那些文档。也许不是在上面的 $http 情况下,但在其他一些情况下,我们可能需要知道操作何时完成。
        • @ceejayoz 是正确的,您的商店当然可以用于在操作完成时提供反馈,有时这是您应该做的。即触发一个动作并在完成时提交突变(通过或失败)。 Vuex 是响应式的,因此可以很容易地观察状态。然而,Mani 的想法也是有效的,因为它提供了链 Promises 的能力,这使得工作流程更加清晰,并且还可以在完成之前提交突变。因此,在完成 Promise 时,您知道状态是正确的,因为您已经调用了同步突变。
        • @DanielPark 是的,“这取决于”场景和个人开发者偏好。就我而言,我想避免在我的状态下使用像 {isLoading:true} 这样的中间值,因此求助于 Promises。您的偏好可能会有所不同。归根结底,我们的目标是编写整洁且可维护的代码。 Promise 是否实现了该目标,或者 vuex 状态 - 由各个开发人员和团队来决定。
        • @Mani 天哪,你是对的,在制作小提琴时想通了。非常感谢!
        猜你喜欢
        • 2021-05-03
        • 2018-01-20
        • 2019-07-30
        • 2019-05-08
        • 2019-02-13
        • 2018-11-11
        • 2019-11-02
        • 2020-08-29
        相关资源
        最近更新 更多