I have a Vuex getter like this:
sectionsAndSubSectionsJoined(state) {
var sections = state.aBMudbone.sections;
var subsections = state.aBMudbone.subSections;
var combinedSections = [];
_.forEach(sections, function (section) {
var subSectionsFiltered = _.filter(subsections, ['sectionId', section.sectionId]);
var sectionAndSubSections = section;
sectionAndSubSections.children = subSectionsFiltered;
combinedSections.push(sectionAndSubSections);
});
return combinedSections
}
In the console I get the following error:
[vuex] do not mutate vuex store state outside mutation handlers.
The line it shows this error for is :
sectionAndSubSections.children = subSectionsFiltered;
As I understand it, even though sections, subSections and sectionAndSubSections are new objects, because they are created from the store state they are subject to some binding hence the warning when I try to add to this state.
How can I disconnect this new state from the store state so I don't get this warning?
Related
I'm using react js with mobx and I get data from api.
the data I get is array of objects.
when I set the data into mobx variable then I see array of proxy objects(not sure what the proxy says). I'm trying just to set the array of objects I get from api into mobx variable.
my store
class UserStore {
#persist #observable token = null
#observable tasks = []
#observable done = false
#persist #observable email = ''
constructor() {
}
#action
getTasks = async () => {
try {
let response = await Api.getTasks()
console.log('getTasks',response.tasks)
this.tasks = response.tasks
console.log('my new tasks',this.tasks)
} catch (e) {
console.log(e)
}
}
as you can see here in the first block('black') the data i get from api, then i set the respnse.tasks into this.tasks.
this.tasks = response.tasks
console.log('my new tasks',this.tasks)
You can convert proxy to JS:
import { toJS } from 'mobx'
// example
toJS(response)
It depends on how you want to observe the data.
"I'm trying just to set the array of objects I get from api into mobx variable"
is not really your end-goal.
If you want your observers to:
option a: react when the array reference change
= No care for values in the array.
Use #observable.ref tasks.
option b: react when the references of each value in the array change
= No care for the individual objects properties.
Use #observable.shallow tasks.
option c: react on the individual objects properties too
= Make everything observable, references and object properties
Use #observable tasks like you do.
Like indicated in the comments, mobx5 is using Proxy and some behaviour might differ compared to previous version.
More info: Mobx arrays, Mobx decorators, shallow observability
Note: you would need to give more details if this does not help you, like your react component code.
In my case, toJS was not working because I had a class with a variable with the type of another class. So, I needed to create new objects from the JSON:
parsedArray.map(
(a) =>
new MyObj1({
id: a.id,
myObj2: new MyObj2({
label: a.label,
}),
title: a.title,
})
);
If you run debugger, stop the debugger. It messes the mobx's flow.
Vue gives this example of a simple store, where state is modified by calling an action on the store...
var store = {
debug: true,
state: {
message: 'Hello!'
},
setMessageAction (newValue) {
if (this.debug) console.log('setMessageAction triggered with', newValue)
this.state.message = newValue
},
clearMessageAction () {
if (this.debug) console.log('clearMessageAction triggered')
this.state.message = ''
}
}
But state is a public property of store, and no-one is obliged to pass by setMessageAction() to modify it. Is there a simple trick to protect state, but still have it observed by vue? I can't think of one.
JavaScript does not support access modifiers; everything is public.
There are various ways of encapsulating private implementation details in JavaScript, but in your example the store's state needs to be public so that it can be assigned to the component's data.
Vuex has a strict mode which, if enabled, will throw an error if the state is modified outside of mutation handlers.
I'm trying to create an object in one part of vuex store, and then pass id to it to another object, and i'm not sure how to properly do that since mutations can't return returning anything (in this case, id).
Two store objects look like this:
// store/report.js
const state = {
name: 'Untitled Report',
subReportIds: []
};
// store/subReport.js
const state = { ... }
And i'd like this action to create blank report, then blank subreport, and then assign subreport id to newly created report. (subreports are independent entities, and can be used by multiple reports, hence different area in store)
const actions = {
createNewReport({ state, commit }) {
commit(mutationTypes.CREATE_NEW_REPORT)
// below doesn't work - i can't get return from mutation
let newSubreportId = commit(mutationTypes.ADD_NEW_SUBREPORT)
// if this worked, i'd then do something like
commit(mutationTypes.ADD_SUBREPORT_TO_REPORT, newSubreportId)
}
};
How can i achieve the above?
So best way to accomplish to me would be to dispatch actions instead of committing the mutations. If you look at the methods in Vuex source, commit only executes with no return (so is a void) and dispatch returns the value you return from the action (which is a function)
For my actions, i always return a promise so that i can compose them like you mention above. Here is an example.
fetchSomething ({ commit }) {
return mockApiGetIds()
.then(response => {
commit({
type: SOME_MUTATION,
ids: response
});
return response;
});
},
Disclaimer : I don't know if it is truely a good idea, but at least, it seems to work, and to me, it feels prettier than having to use actions and promises, or to generate the id in the action...
With your mutation, you can pass an argument. To return a value from a mutation (like a newly created id), I write it to a placeholder in that argument :
someMutation(state, arg){
//...
arg.out = {
status : "succeed"
}
}
//...
this.$store.commit('someMutation', arg);
if(arg.out !== "succeed") console.log("ERROR");
I'm trying to update an array every time my tag receives an update from parent tags. Here is my code:
var tag = function(opts){
this.set = [];
var self = this;
this.on('updated', function(){
var obj = opts.my_topics;
var better_obj = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
var topic_title = key;
better_obj = obj[key];
better_obj['title'] = key;
}
}
self.set.push(better_obj);
})
}
Whenever I try to push(), I get an error: Uncaught SyntaxError: Unexpected token ; database.js:60. The moment I remove the line of code for the push, the error disappears. Am I using this/self wrong in this case or could it be something else?
What I am trying to accomplish here is taking an object from opts (which receives regular updates), converting into an array, and using it for an each loop within my tag. I do not know how to do this other than a) what i'm trying to do now by accessing this, or b) rewiring my opts to deliver an array rather than an object (which is currently failing for me as I'm using Redux and it doesn't like dispatching non-objects).
I'm trying to write to an object in firebase, here is how i did it:
var myDataRef = new Firebase('https://ootest1.firebaseio.com/users/' + '-J6kDooooooz_eV3Cq' /*JobsManager.getCurrentUser().username*/);
angularFire(myDataRef, $scope, 'newUser');
if (!$scope.newUser.jobs)
$scope.newUser.jobs = [];
The problem is that $scope.newUser is undefined.
Why is that?
The angularFire binding returns a promise, and the $scope.newUser model won't be defined until that promise is fulfilled:
var promise = angularFire(myDataRef, $scope, 'newUser');
// $scope.newUser is undefined
promise.then(function(){
// $scope.newUser is defined
}