从厌恶行为中回归承诺

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

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

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

基本上,从一个操作返回一个承诺(类似)对象被认为是好的实践吗?我将它们视为异步包装器,具有失败等状态,因此似乎非常适合返回承诺。相反,mutator 只是改变了一些东西,它们是存储/模块中的纯结构。

158299 次浏览

Vuex 的 ABc0是异步的。让调用函数(action 的发起者)知道某个操作已经完成的唯一方法是返回一个 ny 并在以后解析它。

这里有一个例子: 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时,它将获得这个噪音对象,并且可以知道它是否成功。下面是 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"]的数据,那么数据流是单向的。

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

组件可以从上面示例中的承诺解析函数获取数据(不是单向的,因此不推荐) ,也可以直接从单向的、遵循 vuex 数据生命周期的 $store.state["your data key"]获取数据。

上面的段落假设您的变异函数使用 Vue.set(state, "your data key", http_data),一旦 http 调用在您的操作中完成。

只是想知道一个封闭话题的信息: 你不必创造一个承诺,公理本身就会返回一个承诺:

档号: 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 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...
}
})

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

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

但是,在 边缘案件中,从你的行动中返回一个承诺可能是“必要的”。

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

愚蠢的例子

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

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

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>