Возвращение обещаний от действий Vuex
Я недавно начал переносить вещи из jQ в более структурированную среду VueJS, и мне это нравится!
Концептуально, Vuex стал для меня чем-то вроде смены парадигмы, но я уверен, что знаю, о чем идет речь сейчас, и полностью понимаю! Но существует несколько маленьких серых областей, в основном с точки зрения реализации.
Мне кажется, что этот дизайн хорош, но я не знаю, противоречит ли он циклу однонаправленного потока данных Vuex.
По сути, считается ли хорошей практикой возвращать обещанный (подобный) объект из действия? Я отношусь к ним как к асинхронным упаковщикам с состояниями сбоя и тому подобным, поэтому мне кажется, что это хорошо подходит для возврата обещания. Наоборот, мутаторы просто меняют вещи и являются чистыми структурами внутри магазина / модуля.
2 ответа
actions
в Vuex работают асинхронно. Единственный способ сообщить вызывающей функции (инициатору действия), что действие завершено, - это вернуть Обещание и разрешить его позже.
Вот пример: 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"]
в ваших компонентах поток данных является однонаправленным.
Обещание от действия - только дать компоненту знать, что действие завершено.
Компонент может либо получать данные из функции разрешения обещаний в приведенном выше примере (не однонаправленный, поэтому не рекомендуется), либо напрямую из $store.state["your data key"]
который является однонаправленным и следует жизненному циклу данных vuex.
В приведенном выше абзаце предполагается, что ваш мутатор использует Vue.set(state, "your data key", http_data)
после завершения http-вызова в вашем действии.
Просто для информации по закрытой теме: вам не нужно создавать обещание, axios возвращает его само:
Пример:
export const loginForm = ({commit},data) => {
return axios.post('http://localhost:8000/api/login',data).then((response) => {
console.log(response);
commit('logUserIn',response.data.data);
}).catch((error) => {
commit('unAuthorisedUser',{
error:error.response.data
})
})
};
действия
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; возвращать обещания от ваших действий только тогда, когда это необходимо, но СУХОЙ цепочкой тех же действий.
Долгое время я также думал, что возвращаемые действия противоречат циклу Vuex однонаправленного потока данных.
Но есть КРАЙНЫЕ СЛУЧАИ, когда возврат обещания из ваших действий может быть "необходимым".
Представьте себе ситуацию, когда действие может быть инициировано двумя разными компонентами, и каждый по-своему обрабатывает случай сбоя. В этом случае необходимо передать вызывающий компонент в качестве параметра для установки разных флагов в магазине.
Тупой пример
Страница, на которой пользователь может редактировать имя пользователя на панели навигации и на странице /profile (которая содержит панель навигации). Оба инициируют действие "изменить имя пользователя", которое является асинхронным. Если обещание не выполняется, на странице должна отображаться ошибка только в компоненте, с которого пользователь пытался изменить имя пользователя.
Конечно, это глупый пример, но я не вижу способа решить эту проблему без дублирования кода и выполнения одного и того же вызова в двух разных действиях.
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>