Are vuex mapGetters even worth using? - vue.js

So I believe the title introduces my question. Given you're introducing a new dependency in each component you want to use mapGetters in, this adds bloat to your package.
This also means your component is now specifically bound to Vue, which is 1 more thing to contend with were you to port components over to another framework like React.
Consider these 2 computed property lists:
import { mapGetters} from 'vuex'
computed: {
...mapGetters({
active:'interface/active',
mode:'interface/mode',
views:'interface/views',
}),
}
vs
computed: {
active:() { return this.$store.getters['interface/active'],
mode:{ return this.$store.getters['interface/mode']},
views:{ return this.$store.getters['interface/views']},
}
Sure the latter is a tad bit more verbose, but nothing overly bad. You've still got all your getter data the same way in either scenario. To be this seems like totally unnecessary fluff for the small benefit of saving a few characters.
The former example weighs in at 207B (201 characters)
while the later example weights in at 207B (201 characters).
To me that means in terms of type savings, there's virtually none to be had. However I do concede that the former is more readable, portable and ultimately easier to maintain as your relevant data is all in 1 spot.

There are a couple of things that I think are worth mentioning here, regarding an opinion you have on the helper function like mapGetters
About your code being tightly coupled to vuex should you decide to import mapGetters: it should be clarified that the approach where you access this.$store.getters directly doesn't make your code any less coupled to vuex. After all, such direct access already does assume quite a lot about store's functionalities and how they're exposed to components.
Take snippets you provided as examples, it can already be seen that your store implementation a) has a concept called getters, and that b) it supports modules such that you can define an interface module like you did.
Suppose one day, you somehow decide to switch store's implementation to something like puex, which has absolutely no concept of either getters or modules, you will have to refactor all 3 declarations anyway. Or even if you decide to switch to something more advanced like Flure, it will also put you in a similar situation: though Flure has concepts of getters and modules, they aren't exposed the same way vuex does, so you have to edit all 3 lines (again).
Let alone porting to React, the code you have in computed properties won't even be portable to other store libraries created specifically for Vue. So, really, you shouldn't worry too much about using mapGetters.
Secondly, for type-saving sake, if you're not going to rename these properties anyway, your mapGetters call can actually be shortened to a one-liner:
computed: {
...mapGetters('interface', ['active', 'mode', 'views']),
}
Third, once you start having multiple lines of these, it's just easier to track all declarations (e.g., how many getters or state fields you expose from each module). So these helper functions actually helps improve maintainability if used well.
computed: {
...mapState('interface', ['list', 'packages']),
...mapGetters('interface', ['active', 'mode', 'views']),
...mapGetters('foo', ['bar', 'baz']),
}
Forth, regarding bundle size, on second thought I believe this might be the main reason why people downvoted you. So, I'll try to explain:
The thing you need to understand is that real-world production webapps these days use build tools like webpack to create javascript bundles. And because of that, javascript you wrote can be pretty much regarded as source code, which is totally different from the code shipped in a bundle. The reason why they are so different is that the bundle can utilize both minify and gzip techniques to achieve drastic improvement in file size (usually more than 70% size reduction in practice).
In other words, for webapps these days, javascript is not being served as-is, but almost always be processed by webpack or the like. So, all the character counting you have been doing on the source code isn't going to make a noticeable difference to the final bundle size at all. And because of this, some people might regard your concern as an extremely bad form of micro-optimization, hence their downvotes.
If I were you, I will try to embrace these helper functions as much as possible, and look at the bundle size after automatic build optimizations before worrying about the bundle size at all. And even if the final bundle size is too big to your liking, I can almost guarantee you won't gain much from mapGetters refactoring, as it's usually other stuff that substantially bloats the bundle size (like a view component library, for example). Really, premature optimization on vuex isn't really worth your trouble.
So, to me, these functions (mapState, mapGetters, mapActions, mapMutations) are definitely worth using.

Related

Incrementing a counter with functional programming vs imperative programming

I likely am asking a very stupid question here, please forgive me.
I am a Java and C# backend-engineer with relatively good knowledge of OOP design patterns. I have recently discovered the debate about OOP vs functional programming and the question i simply can't wrap my head around is this: If there is no state, then how could we update an element based on user input?
Here's a small example showing the problem i am facing (i am aware that JS is not a strictly functional language, but i think it shows my problem relatively well.):
Let's say hat we have a small web page that simply displays a counter and increments its value every time the user clicks a button:
<body>
The count is <span id="data">0</span><br>
<button onClick="inc()">Increment</button>
</body>
Now there's the strictly imperative approach, using a counter variable to store the counter's state:
let data;
window.onload = function(){
data = document.getElementById("data");
}
let counter = 0;
function inc(){
data.innerHTML = ++counter;
}
A more functional approach (at least in my understanding) would be the following code:
let data;
window.onload = function(){
data = document.getElementById("data");
}
function writeValue(val){
data.innerHTML = val;
}
function doIncrement(val){
return val + 1;
}
function readValue(){
return parseInt(data.innerHTML);
}
function inc(){
writeValue(doIncrement(readValue()));
}
The issue i am facing now is that, while the data variable is never altered, data's state still changes over time (every time the counter is updated). I do not really see any real solution to this. Of course, the counter's state needs to be tracked somewhere in order for it to be incremented. We could also call document.getElementById("data") every time we need to read or write the data, but the problem essentially remains the same. I have to somehow track the page's state in order to process it later.
Edit: Note that i have reassigned (which, i am told, is a bad thing in FP) the value val to the variable innerHTML in the writeValue(val) function. This is the exact line at which i am starting to question my approach.
TL;DR: how would you handle data that is naturally subject to a change in a functional way?
This question seems to originate from the misunderstanding that there's no state in Functional Programming (FP). While this notion is understandable, it's not true.
In short, FP is an approach to programming that makes an explicit distinction between pure functions and everything else (often called impure actions). Simon Peyton-Jones (SPJ, one of the core Haskell developers) once gave a lecture where he said something to the effect that if you couldn't have any side effects, the only thing you could do with a pure function would be to heat the CPU, whereafter one student remarked that that would also be a side effect. (It's difficult to find the exact source of this story. I recall having seen an interview with SPJ where he related the story, but searching the web for a quote in a video is still hard in 2022.)
Changing a pixel on the screen is a side effect.
Sending an email is a side effect.
Deleting a file is a side effect.
Creating a row in a database is a side effect.
Changing an internal variable that, via cascading consequences, causes anything like the above to happen, is a side effect.
It is impossible to write (useful) software that has no side effects.
Furthermore, pure functions also don't allow non-deterministic behaviour. That excludes even more necessary actions:
Getting a (truly) random number is non-deterministic.
Getting the time or date is non-deterministic.
Reading a file is non-deterministic.
Querying a database is non-deterministic.
Etc.
FP acknowledges that all such impure actions need to take place. The difference in philosophy is the emphasis on pure functions. A pure function has many desirable traits (predictability, referential transparency, possible memoization, testability) that makes it worthwhile to pursue a programming philosophy that favours such functions.
A functional architecture is one that minimises the impure actions to their essentials. One label for that is Functional Core, Imperative Shell, where you push all impure actions to the edge of your system. That would, for example, include an HTML counter. Actually changing an HTML element stays imperative, while the calculation required to produce the new value can be implemented as a pure function.
Different languages have varying approaches to how explicitly they model the distinction between pure functions and impure actions. Most languages (even 'functional' languages like F# and Clojure) don't explicitly make that distinction, so it's up to the programmer to keep that separation in mind.
Haskell is one language that famously does make that distinction. It uses the IO monad to explicitly model impure actions. I've attempted to explain IO for object-oriented programmers using C# as an example language in an article: The IO Container.

What is the best practice for multiple trivial mutations in Vuex store?

In my Vuex code, I have countless trivial mutations, like:
setOption(state, payload) {
state.option = payload;
}
With hundreds of similar lines of code, it doesn't look to be good. Any better option?
Option 0 (New Recommended)
Consider switching to Pinia, new official state manager for Vue.
Mutations do not exist any more. These can be converted to actions instead, or you can just assign directly to the store within your components (eg. userStore.firstName = 'FirstName')
Option 1 (Old Recommended)
Keep it this way.
Pros:
Standard, well-documented.
IDE support. You can easily find usages of some mutation across your project.
Easy to add more logic when needed, e.g. age should be a positive number, etc...
Cons:
Might look repetitive, so it is easier to skip some non-trivial mutation. However, this can partly be solved by convention to put all trivial to the end of the file\section, as soon as mutation stops to be non-trivial - move it to the top of the file\section.
Option 2
Use a universal mutation :
mutate(state, payload) {
state[payload.property] = payload.with;
}
and call it:
commit('mutate', {
property: <propertyNameHere>,
with: <valueGoesHere>
});
Pros:
Clean mutation section, non-trivial mutations are visually distinct.
Cons:
A little harder to find related mutations, e.g. some can have two or more spaces after property:
More writing for each mutation call.
Not really compatible with a standard way, so you can end with non-trivial mutation with additional logic, but someone can miss it and use a unified mutation call.
Option 3
Use some code generator, e.g. Hygen
Pros:
You have the same code as from Option 1, including IDE support and future extension.
You can set it to add in all places by a single command line - to state, mutation, and action (if needed).
With typescript you also need to change interface of the store, wich can also be done with the same command line.
Cons:
Can be hard to set up.
Need additional informing of new team members.
Option 4
Use some Vuex plugin with predefined mutations, e.g. this one
Pros:
Minimal setup.
Clean mutation section, non-trivial mutations are visually distinct.
Cons:
Needs some knowledge about the plugin.
A little harder to find related mutations
Not really compatible with a standard way, so you can end with non-trivial mutation with additional logic, but someone can miss it and use a mutation call provided by a plugin.

Vue component with input provided by a function vs reactivity

I understand that reactivity is a big deal in VueJs. However, I tend to use OOP for my front end development, and therefore data is often provided by functions. This will be naturally non-reactive, which can be overcome by using this.$forceUpdate.
The question is - is this a fundamentally bad concept? Or is it just fine? Is there a better way? Probably watchers can be used, but I feel like that would make the code more complicated.
Example:
<template>
<multiselect
:value="myclass.getValue('param1)" />
#input="val => valueUpdated(val)"
</template>
Then in methods:
valueUpdated(newVal)
{
this.myClass.setValue(newVal);
this.$forceUpdate();
}
Note, myClass has async data, so by the time of the first rendering, it is not filled with the data. MyClass comes into the component as a prop, and it is a TypeScript class.
this solution works fine, but is there a better (correct) way?
Doing/forcing view to update manually is a fundamentally bad concept. The view should update at the moment your data changes - and it should update automatically. This is what reactivity gives you. This is like going from a car with manual gears and 49 shifts to a car with automatic shifting.
The whole idea is that you define declaratively the behavior of your view - how it depends on the data model. Building the view imperatively is more tedious and error-prone and certainly less elegant.
OOP may work well in non-visual libraries - but for UI composing components with props and events works better.

How to create multiple Stenciljs components with slight differences?

I wrote a Stenciljs component "textarea" with a lot of methods and logic. Additionally I need an inputfield with pretty much the same functionality but some minor differences. I don't want to maintain two components that have nearly the same tests, nearly the same styles and nearly the same methods that would end in a lot of code duplicates.
Right now i created a generic component "idb-input" with a "type" property that can be "textarea" or "textfield" and use if else statements to decide which methods are allowed for this type. Since this is a bit messy i would like to have a "base component" and inheritate from it for example. I already tried to use a generic typescript class but i can't use the decorators and a lot more limitations.
Further this approach isn't that clean in HTML:
<idb-input type="textarea">
<idb-input type="numberfield">
<idb-input type="inputfield">
What is the right way solving this problem?

Performance impact of stacking multiple Vue components

I am building an application that uses Vue to display most of the data that my users will see (charts, tables, etc.)
Now, imagine I have a table built with Vue with a list of actions performed by each user. Now, the user name cell will be especially formatted with the user’s picture, social handle, etc. The way the user is presented in this table is the same as it will be in other components. It will also contain some especific functionality (e.g. hovering over the picture will provide expanded information about the user; but this functionality is only planned).
My objective is to reuse as much code as possible. This is why I was planning to have a small UserProfile.vue component inside my Table.vue component. Using this logic, I may repeat this for other information as well (think CompanyProfile.vue, ProjectDetail.vue, etc.).
Is this a good idea? Will it have cause significant performance issues to have multiple vue components inside one and other?
Vue has optimized the result of rendered html as far as possible. You don't need to worry a lot about it. Child components only update when their respective data has changed, that is usually quite cheap.
Good to keep in mind:
When authoring components, it’s good to keep in mind whether you intend to reuse it somewhere else later. It’s OK for one-off components to be tightly coupled, but reusable components should define a clean public interface and make no assumptions about the context it’s used in.
For larger applications, you may use Async Components
Vue will only trigger the factory function when the component actually needs to be rendered and will cache the result for future re-renders. For example:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the resolve callback
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
I found some interesting topics that you might want to look:
Performance for large number of components
Performance degradation when using components
Unlock performance tracing in vue
Analyze runtime performance
So now, you know what to do. Hope, it helps.