Vue watchers and components - vue.js

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.

Related

Reactive object from a lib and slow reactivity

I am working on a small chess website based on fastAPI and Vue 3 (composition API).
The chess logic is managed by the chessjs lib from which I can instanciate a chess object with a full package of handy methods.
In order to synchronize my component and my chess object, I created a chess reactive:
let chess = reactive(new Chess())
A lot of computed properties and template linked to those properties later, I just figured out that all methods calculation launched on the proxy become very slow compared to the raw object (example, looking for the possible moves from a position takes 3ms with the raw object and 180ms with the proxy). This is independant from the quantity of rendering demanded.
I understand that Vue needs to check what needs a render or an update but I find it pretty awkward.
What am I missing here?
My workaround for now is to use the raw object and use a computed prop which depends on refs hat are updated by some functions that are trigged with event:
click on UI (or whatever other event) => triggers a function that use the raw chess method that returns some array and update a ref => updates a computed prop based on the ref => makes the template update
instead of
click on UI (or whatever other event) => call chess.method() that updates the chess proxy object => makes the template update
I feel like I am not use Vue as it should be...
Can someone give me some advices?
You shouldn't be making the entire Chess instance reactive since it likely contains a significant amount of internal properties, most of which are not necessary to detect a change. Since chess.js doesn't look it has any "listener" methods for detecting a change to the board, the correct solution would be to use a shallowRef (https://vuejs.org/api/reactivity-advanced.html#shallowref) and manually call triggerRef (https://vuejs.org/api/reactivity-advanced.html#triggerref) whenever you make changes to the chess board.

Does using setters still break one way data flow principle?

I'm building a data table component that nests subcomponents about five levels deep. e.g. (simplified) controller, table, thead/ tbody, tr, td.
The top component (controller) contains all the definitions and passes them down.
However, the last component in the chain (td) is performing some calculations on the data and those calculations are then passed to other subcomponents in the render stack.
I really hate to break the one-way data flow principle here, but we're talking tens of thousands of calculations, and hundreds of events bubbling up four levels. Bubbling up manually, of course.
I can do it using event bus pattern or a shared state pattern and I think that would comply with all the recommended practices.
However, I have found that simply declaring a setter on the affected prop attributes and placing deep watches on the props involved (in the few strategic places) results in much faster code.
What is the recommended way of solving a data-flow problem like this?
Edit: asked to provide an example. Hopefully this oversimplification will do:
controller is responsible for declaring table columns. It also contains a property called maxWidth containing the measured maximum td width for the column.
As a consequence components table, thead/ tbody, and tr all contain prop called columns while td contains column as in one column from the columns array.
Say td measures rendered width and reports that back to the controller so that it can decide which columns to hide.
Approach 1: (props down, events up)
td emits event, tr captures it and re-emits, etc. until it is captured by controller which then performs the necessary calculations and thus modifies the columns, triggering a re-render of the entire stack.
Approach 2: (vuex)
The columns array is in storage with action defined for adjusting column measurements?
Approach 3: (property setter)
The columns array declares a setter / method for adjusting maxWidth value.
Note how a method would essentially be the same as in vuex approach while a setter makes the assignment syntax somewhat less obvious that we're using an actual method for changing the state.
Also note how I myself fail to see the difference between vuex approach, column setter method or column property setter... Hence the question.
Edit 2: more clarification
If I'm using a setter, linter will immediately complain about vue/no-mutating-props whereas it won't do so when I'm using a method with basically the same code, except that it isn't a property setter, but a "separate" method.
Would that automatically mean that using a method is OK as it's semantically distinct from assigning a value?
For reference: I have forgone setters since they seemed too obvious and linter complained about property modification.
Instead I have created setter functions, e.g. setMaximumWidth on the object containing the property.
This solution seems like it conforms to best practices, at least as far as I understand them, and doesn't really change the approach.
I'm happy, but will probably get bashed some time for not using something more obvious, whatever that may be.

Is it standard to copy collections out of vuex state to prevent mutation?

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.

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.

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.