Why should I use the action decorator in mobx - mobx

I'm trying to get my head around the usefulness of the action decorator in mobx, even after reading the doc, https://mobx.js.org/refguide/action.html
Still wondering why I should use #action or #action.bound, other than to enforce a pattern where a component cannot change the observable directly.
The above article mentions providing "useful debugging information". But where can I find this info? F12->Console doesn't show anything when calling an #action or #action.bound method.
Or am I doing something wrong in the below code?
Should I install some mobx debugger? Thanks.
class CommentStore {
#observable commentData = [];
#action.bound
updateComment(id, name) {
this.commentData.map(p => p.id === id ? p.name = name : p.name = p.name);
}
...

If you mutate more than an observable variables inside a method which is not decorated by #action, your derivations (autorun) will run multiple time. This issue is not present when you work with react. render function will run only once.
one of the things which #action decorator do is to prevent multiple invocation of your derivations.

Related

vuex-persistedstate not saving class methods

I'd like to preference this by saying my backgrounds in in C# so I like declaring methods within my classes. I've created a user class that contains properties and methods and I've added this to my vuex-persistedstate. One of the methods is a logout() method which clears out the properties. When I tried to invoke this method I got the following error:
TypeError: this.$data.user.logout is not a function
I then reviewed local storage and noted the user did not have reference to the class method. So I went ahead and copied the logic from the method into my vue component and it worked so I'm assuming the issue is vuex-persistedstate does not save references to methods which is why the method call did not work.
I'd like to declare the logout method in one location rather than spreading it out across vue components, what is the best practice for accomplishing this? Is it possible to do this in the class declaration or do I need a user helper file?
Sure Berco! My code is also up on GitHub so you can review it there too, but basically it seems to me that vuex does not store methods. The first file you should review is my user.js file:
https://github.com/Joseph-Anthony-King/SudokuCollective/blob/master/SudokuCollective.WebApi/client/src/models/user.js
In this file I have a method called shallow clone which takes the info received from the API and assigns it to the user:
shallowClone(data) {
if (data !== undefined) {
this.id = data.id;
this.userName = data.userName;
this.firstName = data.firstName;
this.lastName = data.lastName;
this.nickName = data.nickName;
this.fullName = data.fullName;
this.email = data.email;
this.isActive = data.isActive;
this.isAdmin = data.isAdmin
this.isSuperUser = data.isSuperUser;
this.dateCreated = data.dateCreated;
this.dateUpdated = data.dateUpdated;
this.isLoggedIn = data.isLoggedIn;
}
}
You of course don't need to abstract this away but I've found it makes the code easier to maintain.
Then in the mounted() lifecycle hook I assign the user received from the API to the component user via the shallowClone method. Please bear in mind I've done additional work on this project and the login form is now it's own component which receives the user as a prop from the app:
https://github.com/Joseph-Anthony-King/SudokuCollective/blob/master/SudokuCollective.WebApi/client/src/components/LoginForm.vue
mounted() {
let self = this;
window.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
self.authenticate();
}
});
this.$data.user = new User();
this.$data.user.shallowClone(this.$props.userForAuthentication);
},
The full code can be reviewed here:
https://github.com/Joseph-Anthony-King/SudokuCollective
I found a solution... I'm working on improving it. Basically I use the values pulled from localstorage into vuex to create a new user object in the vue component that has reference to the methods located in my user class declaration. I recalled recommendations that we should create clones of objects pulled from vuex for use within the vue component. I'm still refining the code but that's basic idea.

sub-property write commit withing store actions using vuex-pathify make.mutations

Here is how my store looks like:
const state = {
user: {
profile: {
phoneNumber: '',
}
}
}
const mutations = make.mutations(state)
const actions = {
submitPhoneNumber({commit}, phone_number) {
// blah blah
commit('SET_USER#profile.phoneNumber', phone_number);
}
}
But no such mutation can be found.
Maybe I could import store.js within store.js and use the set helper but I believe things can get pretty creepy specially because of the (In my opinion poor) design decision that the library creator has made to combine commit and dispatch (I believe being explicit would have been much better here)
Pathify author here.
You can't commit a mutation with sub-property syntax using Vuex mutations, because Vuex will treat it as a string.
You are correct that you would need to use store.set() to do this.
You can be explicit with commits and dispatches by appending a ! to the call. This is called "direct syntax":
https://davestewart.github.io/vuex-pathify/#/api/paths?id=direct-syntax
To commit directly using the sub-property syntax, use the Payload class:
https://github.com/davestewart/vuex-pathify/blob/master/src/classes/Payload.js
https://davestewart.github.io/vuex-pathify/#/api/properties?id=payload-class
Something like this should work:
import { Payload } from 'vuex-pathify'
commit('SET_USER', new Payload('SET_USER', #profile.phoneNumber', phone_number);
Looks like I haven't documented this, so I have a made a ticket here:
https://github.com/davestewart/vuex-pathify/issues/80
It's using a commit from a component, but should work just the same.
People have asked before if it would be possible to use Pathify-style commits in actions and I said it wasn't, but I've just thought of something that might make it possible.
Follow this feature request for more info:
https://github.com/davestewart/vuex-pathify/issues/79

Correct place to run script in vuex flow?

I have an action, setProducts, which I'm passing a payload which returned from a productSearch() method.
In my Vue component this looks as follows:
let products = productSearch();
this.$store.dispatch('setProducts', products);
However, I'm wondering if the correct way to do this would be to refactor my setProducts action to searchProducts then in my mutator call the productSearch() method.
So from my component I run:
this.$store.dispatch('searchProducts');
Then for my mutator method I'd have something like this:
searchProducts(state) {
state.products = productSearch();
},
Is there a more "correct" way to do this?
I think it would be better like this:
Component:
this.$store.dispatch('searchProducts')
Action:
searchProducts(state) {
commit('updateProducts', productSearch())'
}
Mutator:
updateProducts(state, products) {
state.products = products
}
Actions shouldn't directly affect state's values.
You always need to use commit and mutators to update your store variables, and your mutators should never call functions, they work like setters (receive a variable as paramater and apply it to the state variable).

How to call a shared helper function from within a mutation or action in Vuex

I am trying to separate out some code that is common among many calls in my Vuex mutations. I am getting the feeling that this is discouraged but I don't understand why.
Have a look at an image of some sample code below:
I have added this 'helpers' entry in the Vuex - this obviously doesn't exist but how can I call the shared helper function 'getColumn' from mutations and/or actions?
Or do I have resort to calling a static method on a 'VuexHelper' class? :(
Something like:
Note
I have already looked at the following:
Vue Mixins - yes, something like that could work but is not
supported in Vuex - also, vue methods don't return a value...
I have looked at Modules but these still don't give me what I need, i.e. a simple re-usable function that returns a value.
Thanks
I don't see why you may want to put the helper function within the store. You can just use a plain function.
function getColumn(state, colName) {
// Do your thing.
}
const vstore = new Vuex.Store({
// ....
mutations: {
removeColumn(state, colName) {
var column = getColumns(state, colName);
}
}
};
On the other hand, if you really need that, you can access the raw module and all that's included:
var column = this._modules.root._rawModule.helpers.getColumns(state, colName);
Although this syntax is not documented and can change for later versions.
You can implement your Vuex getter as a method-style getter. This lets you pass in the specific column as an argument:
getters: {
getColumn: state => colName => {
return state.columns[colName] || null
}
}
Then getColumn can be used within the store like so:
let column = getters.getColumn('colNameString')
vuex docs > getters > method style access

Callback on transaction or commit()

How can I achieve something similar like that?
App.saveManyDifferentModels()
App.store.commit(function() {
$("#spinner.gif").hide()
});
There is no generic hook at that moment on the transaction that would allow that.
However, if you are persisting a single record, you could add use the didCreate or didUpdate hook on the model.
Another option could be to handle that while changing state/route on the app.
Actually there is a way to do this. For example in your controller you can add a 'didUpdate' method to your model, like this:
var post = this.get("model");
post.didUpdate = function() {
$("#spinner.gif").hide();
};
this.get("store").commit();