So back into it after a while ๐- happy new year everyone! Why this post? Simply because as a company we get to experience so many unique problems and need to come up with new patterns that help solve the problem.
So what is the problem?
We needed to work out an extremely easy way to subscribe to active HTTP requests, for global loader states and other reasons.
If we were using REST as an HTTP protocol layer, then we could have used axios' interceptors to simply commit a mutation to a vuex module tracking the state of an HTTP event. We could do this by creating an object where the keys represent the HTTP event name (something unique) and the value is something truthy, a binary 1 or just some string.
Then we subscribe a button or a loader to that object to find the corresponding active state it's responsible for. This decentralises the necessity to ever actively code each button or modal to maintain a loader state and instead gives us a chance for writing code in a DRY way.
HOWEVER...
Not everything was that simple for us because we're using graphql and apollo, and we're doing it in a non-usual way, where we're using apollo GQL, but not apollo state, so the GQL requests are made using apollo mutations and queries but the data response is stored in plain Vuex.
The main issue was not the apollo state, the issue was that, atleast for now, we couldn't track how to intercept GQL queries in the same way as an axios interceptor and mutate Vuex each time with the right metadata, so a super simple solution we used was.... drum roll ... creating proxy actions.
Whats a proxy action?
Its quite literally what it says, it a proxy to the main event.
async proxyAction({ dispatch }, { actionName, data, setLoading = true }) {
dispatch('setLoading', setLoading, { root: true });
try {
const response = await dispatch(actionName, data, { root: true });
dispatch('setLoading', false, { root: true });
return response;
} catch (e) {
dispatch('setLoading', false, { root: true });
this._vm.$toasted.error(BE_API_ERROR_MESSAGE);
return false;
}
}
The above code snippet shows we dispatch a setLoading action who's job is to toggle the active HTTP request binary value to 1 or 0 (and we subscribe our loader states to this). In addition, we also dispatch the action that was meant to be executed, which is the GQL HTTP query.
So this way, all across the project, we don't need to add multiple lines in the mapActions spread operator within the methods object of a VueJS file's JS section. Instead you just need to do the below:
...
methods: {
...mapActions({
proxyAction: 'proxyAction',
}),
}
...
And whenever a CTA is called or an action needs to be executed, you can simply do the below
methods: {
buttonCalled() {
await this.proxyAction({
actionName: 'user/followVaibhavOnLinkedin',
data: {
url: "https://linkedin.com/in/vaibhavnamburi",
},
});
}
}
This way, as mentioned above, you don't need to have multiple lines in the mapActions spread for all the actions you need, instead, you can simply add it via text and call whatever actionName you want to call and pass whatever data you want to pass!
Pros of using proxy actions:
- Global Button loaders
- Buttons can be disabled to make another API request if one is in progress(For example, clicking on signup button twice is prevented)
Hopefully, this helped you out!
If you think this is a helpful tip please feel free to share it around for others as well on twitter/ youtube or wherever you'd like ๐ and feel absolutely free to tag me too! (@veebuv )
PS if this pattern is common, then I definitely wasn't aware of it in the VueJS/Vuex community, but in the case it is, I'd love to be tagged to the original discussion so I can see how they've done it!
PPS: Special thanks to Dinesh for being pivotal in the experimentation of ideas and implementation of this!
twitter: twitter.com/@veebuv
linkedin: linkedin.com/in/vaibhavnamburi
instagram: _veebuv
Top comments (3)
Good article and nice extrapolation for a global proxified Vuex action, but this has one drawback as far as I see. What happens when you need to have more than one global state loading button on the same page?
Hey mate!
Thanks for the comment and great question!
If you want to build a system where there's multiple loading states, instead of following a binary toggle function, I would do what was mentioned in the beginning of the blog and have an object with key value pairs of the async actions that are currently active and have loader states subscribe to those actions
Great post. Thanks for the tag Vaibhav. Proxy actions are a sure lit ๐ฅ