Vuex: Does dispatch return a promise so that you can chain them? - vue.js

I am not coding my app yet so I can't test it out but if I wanted to chain dispatches, is that possible to make my dispatches run synchronously?
example:
this.$store.dispatch('doSomething', {
'url': '/admin/do-something',
'user_id': userId,
})
.then(response => {
this.$store.dispatch('doSomething2', {
'url': '/admin/do-something-2',
'user_id': userId,
})
})

Answer
According to the vuex documentation:
dispatch
dispatch(type: string, payload?: any, options?: Object): Promise<any>
dispatch(action: Object, options?: Object): Promise<any>
Meaning, dispatch will always return a Promise so, theoretically, yes you can chain the dispatches to ensure a specific order
Extra
Going further, if these actions always need to happen in this order, consider making a single action that will dispatch each of these individually for you:
someVuexModule.js
...
export const actions = {
async doSomethingMaster({ dispatch }) {
await dispatch('doSomething');
await dispatch('doSomething2');
await dispatch('doSomething3');
},
doSomething() {},
doSomething2() {},
doSomething3() {},
}
...

Related

How to filter type-graphql subscription using context, instead of args?

I am trying to send notification to different users and want every user to see only his. I have already succeeded to do you with #Args.
Heres the subscribtion:
#Subscription(() => Notification, {
topics: "NOTIFICATIONS",
filter: ({ payload, args }: ResolverFilterData<Notification, NewNotificationArgs>) => {
return payload.userId === args.userId;
},
})
newNotification(
#Root() notification: Notification,
#Args() { userId }: NewRequestArgs
): Notification {
return notification;
}
And the mutation:
#Mutation(() => Boolean)
async createNotification(
#PubSub("NOTIFICATIONS") notifyAboutNewNotification: Publisher<Notification>
): Promise<Boolean> {
const notification = await Notification.create({
userId: <some id>,
message: <some message>
}).save();
await notifyAboutNewRequest(notification);
return true;
}
Now I want you use a userId, that I have stored in the context.req.session.userId as a cookie.
ResolverFilterData contains payload, args, context and info. So you can access that by filter: ({ context }) => { ... }.
I logged my context and It looks like I was getting the values, but undefined. You need to use the onConnect event to upgrade the context.
Links than helped me:
Discussion
Youtube

How to pass info from inside of object inside a state

I am using Nuxtjs and I would like to take an id that is in my state and use it to make a new call to an API.
My carInfo has an IdAddress that I would like to use to call the API.
My store:
export const state = () => ({
allCars: [],
carInfo: [],
carAddress: []
});
export const actions = {
async fetchAll({ commit }) {
let cars= await this.$axios.$get(
"apiaddress"
);
commit("setCars", cars);
},
async fetchcar({ commit }, id) {
let car= await this.$axios.$get(
"apiaddress"
);
commit("setcar", car);
},
async fetchAddress({ commit }, id) {
let address = await this.$axios.$get(
"apiaddress"
);
commit("setAddress", address);
}
};
The Actions documentation says:
Action handlers receive a context object which exposes the same set of
methods/properties on the store instance, so you can call context.commit to
commit a mutation, or access the state and getters via context.state and
context.getters.
And it goes on to say that they often use argument destructuring which is why the parameter to actions often looks like this: { commit }.
In your case you could add state to the parameters and then you should be able to access your carInfo value from the state.
For example, change your fetchAll action to:
async fetchAll({ commit, state }) {
let cars = await this.$axios.$get(
apistate.carInfo.IdAddress
);
commit("setCars", cars);
},

Returning data from a Vuex action

Can I return data from a Vuex action or do I need to update the store?
I've got an action defined but it returns no data:
getData() {
return { "a" : 1, "b" : 2 }
}
You can actually return data from an action. From the documentation:
Actions are often asynchronous, so how do we know when an action is
done? And more importantly, how can we compose multiple actions
together to handle more complex async flows?
You should return a promise and the data in the resolve() method:
actions: {
actionA () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ name: 'John Doe' })
}, 1000)
})
}
}
And use it this way:
store.dispatch('actionA').then(payload => {
console.log(payload) /* => { name: 'John Doe' } */
})
Your code will work if the code that calls your action uses either 'await' or a callback.
For example:
const result = await store.dispatch('getData');
The main takeaway being that actions return promises even if the action isn't doing any asynchronous work!

How can my vuex mutation dispatch a new action?

How can my vuex mutation dispatch a new action or how can my action get read access to the store?
Basically I've got a action that calls an mutation:
updateSelectedItems: (context, payload) => {
context.commit('updateSelectedItems', payload);
},
And the mutation that updates the list. It also gets any new items. I need to do something with these new items:
updateSelectedItems: (state, payload) => {
var newItems = _.differenceWith(payload, state.selectedItems, function (a, b) {
return a.name === b.name;
});
state.selectedItems = _.cloneDeep(payload);
_.each(newItems, (item) => {
// How do I do this??
context.dispatch('getItemDetail', item.name)
});
},
It's really not best practice to make your mutations do too much. It's best if they're super-simple and generally do one thing. Let your actions take care of any multi-step processes that might affect the state.
Your example would make more sense structured like this:
actions: {
updateSelectedItems(context, payload) {
var selectedItems = context.state.selectedItems;
var newItems = _.differenceWith(payload, selectedItems, (a, b) => {
return a.name === b.name;
});
context.commit('setSelectedItems', payload);
_.each(newItems, (item) => {
context.dispatch('getItemDetail', item.name)
});
},
getItemDetail(context, payload) {
// ...
}
},
mutations: {
setSelectedItems(state, payload) {
state.selectedItems = _.cloneDeep(payload);
}
}
If you really need to dispatch something from inside a mutation (which I'd highly recommend not doing), you can pass the dispatch function to the mutation as part of the payload.
It is technically possible using this to call dispatch or commit (or few others) ... i'm only mentioning this for anybody who comes here and needs it for their specific use case.
In my situation i'm using fiery-vuex library which actually passes a function as the payload that will return the updated data, i use this along with a refresh_time key in the db to determine when to refresh certain user data
SOME_MUTATION( state, getData() ){
const new_data = getData()
if( new_data.refresh_time > state.user.refresh_time ){
this.dispatch( 'refreshFromOtherStateMeta', state.user.id )
}
state.user = new_data
}

Calling two mutations from the same action

I'm using a Vuex store to keep all the items in a shopping cart.
There's two actions on the store :
getCartContent, which gets called on page load (fetches the initial content from the backend, which in turn retrieves the data from the session)
addToCart, which is dispatched by the <Products> component when the user clicks the add to cart button.
Both of these call a respective mutation (with the same name), since you're not supposed to call mutations directly from within components.
Here is what the store looks like :
const store = new Vuex.Store({
state: {
items: [],
},
mutations: {
getCartContent(state, data){
axios.get('/api/cart').then(response => {
state.items = response.data;
});
},
addToCart(state, data){
axios.post('/api/cart/add', {
item_id: data.item,
});
}
},
actions: {
getCartContent(context){
context.commit('getCartContent');
},
addToCart(context, data){
context.commit('addToCart', {item: data.item});
}
}
});
This is working as expected, but now when an item is added to the cart (with a dispatch to the addToCart action from within the component), I would like it to call the getCartContent mutation just after so that it fetches a fresh list of items from the backend.
I tried commiting the second mutation from the same action, like this :
actions: {
// ...
addToCart(context, data){
context.commit('addToCart', {item: data.item});
context.commit('getCartContent');
}
}
But that doesn't always work, sometimes it will fetch the items but not always.
I also tried dispatching the getCartContent action from within the component itself, right after dispatching the addToCart action, but it's the same problem.
How can I solve this?
Your axios calls are asynchronous, meaning that your addToCart mutation might not necessarily be finished when your getCartContent mutation fires. So, it's not surprising that sometimes getCartContent doesn't return the items you told axios to send a post request for immediately prior.
You should move asynchronous calls to the vuex actions:
actions: {
getCartContent(context, data) {
axios.get('/api/cart').then(response => {
state.items = response.data;
context.commit('getCartContent', response.data),
});
},
addToCart(context, data) {
axios.post('/api/cart/add', {
item_id: data.item,
}).then(() => {
context.commit('addToCart', data.item)
})
},
}
And your mutations should do nothing but make simple, straight-forward changes to the module state:
mutations: {
getCartContent(state, items) {
state.items = items;
},
addToCart(state, item) {
state.items.push(item);
}
}
The above explanation assumes that instead of making a get('/api/cart') request after each POST request, you would just keep track of items by pushing the data to the state.items property.
If however, you really want to make the GET request after adding an item, you can just get rid of the addToCart mutation and dispatch the getCartContent action after the POST request finishes:
addToCart(context, data) {
axios.post('/api/cart/add', {
item_id: data.item,
}).then(() => {
context.dispatch('getCartContent');
})
},