Is it standard to copy collections out of vuex state to prevent mutation? - vue.js

I’ve scoured the vue forum and there’s a lot of answers that are 2 years old and close, but I’m having a hard time getting one specifically addressing this (I’m simplifying the example):
I have an array of objects in state (row data for a table)
And a tableComponent with subComponents which for-loops through the data and creates one row per item in the collection
The requirement is to add an input to each row in the table which is bound to rowData.foo
The tableComponent has a computed property that gets rowData from state, puts those objects into a new (modified) array, and passes it into the tableComponent template
Which then adds the input with a v-model of rowData.foo
This works, but I recently realized that it is modifying the foo property of a rowData item in the collection without committing a mutation.
I’m ok with dropping v-model and using #input to commit the change, but I have two questions about how this should work
If I want to block these changes until I hit a “confirm changes” button, is it standard / performant to
_.cloneDeep the whole collection in either the tableComponent computed property or in the vuex getter. It seems like a lot of overhead but maybe I’m being too conservative about that?
Allowing v-model to update RowData.foo directly means each row knows which RowData item to modify, now that I’m committing a change to a single object in a vuex collection, is the best practice to make the vuex mutation _.find the object, change it, and then spread the whole collection back into the store?
As with most of my other vue questions, I have multiple ways that make it work, but I’m not sure what the most performant/best pattern is. Thanks for any help!
Update
Simple codeSandbox here: https://codesandbox.io/embed/vuex-store-olrvk
See how the vuex data is updated without an action call?

After reviewing your codeSandbox sample i found that YES your store state data rowCollection is getting mutated without using any mutation and that's because of the v-model (two-way binding) that detects the data spot in memory and mutates it behind the scenes ... of course this was allowed by Vue devs even tough i couldn't find about this at any document (and by the way on the doc they showed an example of a state mutation using v-model but they used a store mutation for that )
and concerning what is the most performant/best pattern i think this way is the easiest and much cleaner (less code)

If you need to buffer user changes to a reactive model (for a commit operation), you will need to make a deep copy. There is no way around it. Totally normal.

Related

Is it bad to use a watch in a child component?

In my project, I need to know the changes in the child components in real time from the parent component.
So I'm going to use watch in a child component to emit event to the parent component whenever the data in the child component changes.
Is this a bad way? I'm afraid there's an unnecessary overload in this flow.
If you have a better way, please recommend it.
There's nothing wrong with this approach. Vue is very efficient in the way it detects changes, and there shouldn't be an unnecessary overload, unless you watch more than you need to. You shouldn't notice any performance issues unless you have a huge object graph (eg thousands of objects being watched).
Make sure you only watch the properties that require change detection, and only use deep:true if you really need to.

Vue - use vuex instead of eventbus for calculations with nested data & components

I have app when bind big JSON object into component, then some parts from this object into next components etc. - it's structure with many deep levels, but object is not copied, I use advantage that objects are passing by reference.
Components on the lowest level have fields like "price", "qty" etc. When user modifies them, I updated object and run recalculation using global eventbus - after recalculation is done, I also use eventbus to forceUpdate some components. For example parents of these with fields price/qt, to refresh "total" amounts in categories.
Now I move some code to vuex and consider also here. Think that recalculation after commit will be ok. The question is - how can I modify this big object using commit from children components? The problem is that commit must "know" what part of object has been modified (for example, one element inside one of many categories)... I can do it in other way, pass child and parent data in commit and update parent but... will it work? I also need reference to do this in proper way...
Maybe still use binding to pass elements, but call store action to only make recalculation (not sure, that provides automatic refreshes on all required modules).
Or maybe other, better solution?
I think you have some problems with architecture. Main idea here is to have some container (smart) component, that is connedcted with store (vuex), and simple (stupid) components, that recieve data from props. Also you must divide your store into modules, so it'll be easy to maintain. This approach will allows you to modify exactly pieces of data you want.

Why should I use getters and mutations instead if getting/setting value direct?

What is the difference between:
this.currentLanguage = this.$store.getters.currentLanguage;
and this for getters:
this.currentLanguage = this.$store.state.currentLanguage;
Also this:
this.$store.commit('setLanguages', this.languages);
is gives me same results with this in mutations :
this.$store.state.languages = this.languages;
I'm receiving what I want in both cases. And getting value direct is easier because I don't need to write getter/mutation.
Why should I use getters/mutations?
you do not need to use getters in this case. getters are like computed properties on a regular Vue component, so you only need them when the data is a more complex descendant of the other data.
you want to use mutations, however, because even if it looks like the this.$store.state.languages = this.languages is working, it isn't necessarily updating it correctly. if you have other components depending on the store, it may not receive the new data because it hasn't undergone the full mutation process.
When you use mutations you have a clear record of what changed in things like vue-devtools. It also allows for time travel debugging.
This is really nice when you're debugging.
The state vs getters is a lot more of a gray area. I personally like not having to think if something is in state or a getter. If I need to get something from the store I use a getter.
That question got answered pretty well by the Vuex Docs itself:
Again, the reason we are committing a mutation instead of changing store.state.count directly, is because we want to explicitly track it. This simple convention makes your intention more explicit, so that you can reason about state changes in your app better when reading the code. In addition, this gives us the opportunity to implement tools that can log every mutation, take state snapshots, or even perform time travel debugging. https://vuex.vuejs.org/guide/ - nearly bottom
So basically the idea is to explicitly track changes. This allows other tools to attach themself to those mutations and actions and track your code which is a huge benefit for large scale applications. It will also make your code more readable and at a later stage much easier to update. Ever read into an old project where they always changed on the go without one "way" to do it - it won't happen here.

Vue watchers and components

Vue newbie here - we are introducing Vue into an existing application bit by bit and therefore I am slightly constricted by this with regards to the back end. The Vue template gets an array of objects from the Django backend. Some of the objects returned are parents and others are children of the parents - identified by the parent_id.
These are rendered happily using various components to encapsulate some of the functionality.
The update end point for the back end is configured to take one single object and update the database accordingly, therefore the watcher must be able to identify which of the array of objects has been updated. From my understanding this isn’t possible for objects/arrays in Vue/Js as the before/after values aren’t available for anything other than simple types.
The workaround I have put in place is to clone the array of objects in a computed property, and then compare the new array to the computed array to identify which object has been modified in order to send that back to the back end.
Is this best practice, or am I going to fall foul to this decision later on down the line?
many thanks for your help
It would be better if you give the objects of the array their own components, so you have a vue component that takes as a prop the object and you then use a v-for on the array to create one instance of the "object component" for each object in the array.
Then you can have a watcher on the object prop in the object component and you will know exactly which object has changed and get better control.

What is the difference between watch and computed methods in vuejs

They both seem to do the same thing, and I can't tell when to use which
Computed properties are just like the data properties. The literal meaning of computed is 'calculated using given values'.
Just as the meaning suggests computed properties are a calculated result of its dependent values(in vuejs data properties, props)
for example:
data:{
lowerCase: 'abcd'
},
computed:{
uppercase(){
return this.lowerCase.toUpperCase();
}
}
in the above example the computed property uppercase is dependent on data property lowerCase. It converts(computes) the letters into uppercase.
whenever lowercase changes , so any template bindings using this computed property automatically update.
Watch properties are more general and in one way more powerful(bit expensive) to watch for changes in data properties.
You can perform complex functions, asynchronous tasks in watchers. The example given in the documentation is a good example of using a watcher.
Summarizing the differences:
Computed properties unlike watched properties should return a value.
computed properties are just like data properties and can be used
in template using {{ }} but watchers cannot be used. Watchers should perform logic to update the data properties which are used in the template.
Computed properties are basically “virtual” properties that are evaluated when they are first used. They will be automatically cached until changes in the component require a reevaluation of the property.
Watch properties are just a mechanism to detect changes in properties, allowing you to perform custom logic.
Since watchers are much more powerful, you can use them to do the same that computed properties do: Basically, you would watch all dependent properties and whenever a value changes, you would recompute the property and store it in the Vue instance’s data.
Unless you require complex logic, computed properties will already do this in a more declarative way: You do not need to listen explicitly on all dependent properties but rely on Vue to automatically detect which properties your computed property depends on. So you just declaratively state how to compute the computed property and don’t need to worry about something else. Computed properites also already come with a good caching mechanism, something you would have to do yourself with watchers.
See also the guide on computed properties and watchers. One example they give for watchers is a debounced service call that would load additional data.
In general though, the gist is: Try to use computed properties for everything. If it won’t work as a computed property, use a watcher.
Its difficult to express this more clearly than the doc
tldr; computed properties are reactive. Their output is cached, and automatically updates if anything used in the function changes. Changes flow out onto the page, without us needing to worry about when or why. watch is "imperative and repetitive". It runs when the thing you're watching changes, like a listener. You then are responsible for how the result is stored and used.
You can do everything with events, but you should try to move up the food chain if you can. Prefer computed to watch.