Only activate Vue getters when not undefined - vue.js

I'm not sure how to better phrase this title... =x But here's my issue.
I have a getter in my vuex store like so,
myTurn( state ) {
if ( state.roomObj.next !== undefined ) {
return state.roomObj.next === state.uid
}
},
The state.roomObj.next is something that is sometimes available, and sometimes not. That is why I have to check if it is undefined or else it is going to cause an error in trying to get the value and hang the application.
But the problem is I have a whole list of such getters and I have to check for undefined for each... It feels a little wrong when all my getters have an extra if statement... My question is,
shouldn't it be by default only processing if it's not undefined. Or shouldn't it be a non-breaking error and just return a NaN or something to the getters' result?
Or is there a better way to check for undefined property so I don't have to have an if statement for every getter?

This should work:
myTurn( state ) {
return state.roomObj['next'] ? state.roomObj['next'] === state.uid : false
}
maybe this also:
myTurn( state ) {
return state.roomObj['next'] && state.roomObj['next'] === state.uid
}
But:
The state.roomObj.next is something that is sometimes available, and sometimes not.
This is really bad. Improve your app that state.roomObj.next will be always available, but sometimes false, sometimes true. Anything you need, but always defined. And you can remove this unncessary condition in all your getters.

Related

store a ref-ed variable in another variable without losing reactivity

I'm unsure if this question have already been asked but i am going to ask it anyway.
So i am using the useAsyncState from #vueuse/core and it works just fine.
Given this piece of code
const { isLoading, error, execute } = useAsyncState(
support.storeSupportTicket,
undefined,
{ immediate: false }
)
given that support.storeSupportTicket returns a promise, which in case of failure, returns an http response with a nested object that contains validation errors so that
there is always an object with error.response.data.errors
If i use it like this, the errors prop is fully reactive.
<TextInput
:errors="error?.response?.data?.errors?.message"
/>
However, since the errors object is nested deeply, i wanted to save some lines by storing it in a variable with a shorter name, but doing it like this makes it lose reactivity and causes the errors prop to not update on change
const validationErrors = error?.response?.data?.errors
...
<TextInput
:errors="validationErrors?.message"
/>

Can anyone tell how to speed up my vuejs app?

I am a noob in vuejs. This piece of my code is making my app very slow.
<div v-for="(attribute, i) in attributes" :key="i">
<div>{{ AttributeClicked(attribute) }}</div>
</div>
This is the function:
AttributeClicked(attribute) {
this.$store.commit("entities/Attribute/select", attribute.id);
}
This is the mutation:
mutations: {
select(state, id) {
let selection = Attribute.find(id);
Attribute.update({
where: (a) => a.selected,
data: {
selected: false
}
});
if (selection !== null) {
Attribute.update({
where: id,
data: {
selected: true
}
})
}
},
}
The purpose of this code is to make a webpage like this one https://www.tesla.com/models/design#overview
My objective is for example to show the 5 options below Paint attribute when the page loads.
Can anyone tell me how to speed up this app?
You might need to provide more code or info to get to what you want to be doing, but with the code provided I can see several issues. Maybe understanding the problems will help you get to the solution you are looking for.
You've got a function inside the template, these are fine to pass to event handles such as #click, but they can have a negative effect on performance. Whenever you have the template re-render (which happens when certain data changes) it will re-run the functions. In this case you run the function as many times as you have attributes, and if any if the AttributeClicked method causes a reactivity update to propagate to this template, you will have an endless loop.
looks like you're calling a mutation when an action may be more appropriate. Mutations are strictly for updating state in a synchronous manner. The select mutation does not mutate the state, so it's simply wrong to put it in there. Even though it may work, vuex is a tool that not only stores global state, but also organizes it in an opinionated way. If you're going to go against the intended design, you may find it easier to just avoid using it.
I suspect you may be able to execute AttributeClicked(attribute) one time during component mount.

how blur or focus vuejs component based on a vuex state

I've got a little problem with vuejs.
My state is basically something like this.
state: ()=>({
activeSide : "from",
}),
I want a component to be focused or blurred based on whether activeSide has the value activeSide set to "from" or not
my idea at the moment, doesn't seem to be very elegant, I basically created a computed property in my component,
focusSide(){
console.log("changed active side")
this.$store.state.activeSide
}
and then I set up a watch to see if that property changes.
watch:{
focusSide : function(newV,_){
console.log("changing focus")
newV=="from" ? this.$refs.fromArea.$el.focus() : this.$refs.fromArea.blur()
}
},
the problems here is that apart from the fact that the solution doesn't look elegant the watch doesn't work either, I've seen that focusSide is changing its value correctly (or at least the body of the method is executed), but the watcher is not executed, I have the feeling that since focusSide state is never used in my template, vuejs thinks that it's not necessary to react and change values, something like reactive frameworks where if the value is not observated then don't change (maybe I'm wrong)
what would be the ideal way for achieve this???
thank you
You need return value of computed properties focusSide, otherwise it will always return undefined
focusSide () {
console.log("changed active side")
return this.$store.state.activeSide
}

Reactivity trigger for Watching computed properties in Vue

Normally when working with Vue, I expect the callback for a watched property to be triggered only when the value of that property changes. However, a colleague noticed that this does not seem to hold when watching computed properties, as can be demonstrated by the following example:
<div id = "demo">
{{ numbers }} </br>
{{ evenNumbers }}
</div>
<script src="./vue.js"></script>
<script>
var demo = new Vue({
el: '#demo',
data: function(){
return {
numbers: [1,2,3,4,5,6]
};
},
computed: {
evenNumbers: function () {
return this.numbers.filter(x => (x % 2 == 0))
}
},
watch: {
evenNumbers: function (val) {
alert("yes, computed property changed")
}
}
})
setTimeout(() => { demo.numbers.push(7) }, 5000)
</script>
After 5s, the alert is displayed, but the value of the computed numbers array doesn't change. It's tempting to infer that the watcher is triggered if the dependencies of the computed property update, even when the computed property itself doesn't.
It turns out that this suits us fine for the application we're working on, but I don't understand the behaviour, and I don't know if we can rely on it, or under what conditions it will hold. (For example, I have two arrays here, but would it still work if I had primitives involved instead? I have no idea, and I might experiment if I have time, but issues with comparing object equality were just the first thing that occurred to me as I typed this, and the pitfalls with Vue's reactivity and composite objects were the second.) I'd also imagine it might be an unpleasant surprise if the callback to your watcher were an expensive operation.
If anyone could explain how this works, and if we can rely on this behaviour, I'd be grateful.
Every time evenNumbers() is executed, it generates an entirely new array. Since arrays are compared by equality of reference, they can never be equal. The only way to properly detect this change is to manually compare the contents of the previously calculated array to the newly calculated one.
Example, using lodash:
import { isEqual } from 'lodash';
...
watch: {
evenNumbers(newValue, oldValue) {
if(!isEqual(newValue, oldValue) {
alert('callback')
}
}
}
The watcher was triggered because it had no way of knowing whether the change on data.numbers will affect the result of computed.evenNumbers.
However, upon recalculating, it discovers that 7 is not even, so the array remains [2, 4, 6].
If you want to make sure the callback only runs when the value actually changes, you can designate it like
watch: {
evenNumbers(newValue, oldValue){
if(newValue !== oldValue) {
alert('callback')
}
}
}

Redux Reducer: key injection into state

First, I do understand this is a mutated state change; I know shame on me. Sometimes the correct way doesn't appear to be the smartest or I at least don't know any better.
Let me explain, I wan't to inject keys into my state without creating and exchanging old state and new state for predefined items. I simply want to inject keys for a while and if I don't then they are all cleared anyway. My initial state looks like
{graphSite: {site: "default", graphBrowser: { graphID: '', graphKey:'' }, browsable:[] }};
my reducer looks like this **using babel es7 object spread operator
case BROWSER: { state.graphSite.browsable[action.id] = action.payload
return {...state}
}
So this will work but redux dev tools doesnt show the state change though the browsable array is accessible perhaps this is because I went against the grain though if I extend the changes to:
case BROWSER: { state.graphSite.browsable[action.id] = action.payload
return {...state,
graphSite: {...state.graphSite,
graphBrowser: {...state.graphSite.graphBrowser},
browsable: {...state.graphSite.browsable} }
}
}
now browsable shows correctly in the tool, but ideally I these are the same thing.
So the question is what is the best way to manipulate the state for dynamic keys that are not in the initial store state. This works just feels like I'm doing something wrong because I manipulated the original state.
The title confused me in the first place. dynamic key injection sounds way too fancy for me :P.
Anyway, you actually want to add a new pair to your browserble.
You had:
case BROWSER: { state.graphSite.browsable[action.id] = action.payload
return {...state}
}
This is wrong (assuming state comes form reducer parameter), because you are directly modifying the state object.
One of the principle of redux is that Changes are made with pure functions. (redux docs).
FTFY:
case BROWSER: {
const nextState = JSON.parse(JSON.stringify(state)); // deep copy your state
nextState.graphSite.browsable[action.id] = action.payload
return nextState
}
This isn't most effecient code, JSONoperations are expensive. It is just to demonstrate the principle of not mutating the state object directly.
Alright, I couldn't see in the context of the object spread how to append variable keys and without created initial store states. This is pretty straightforward after copying the state then returning the appended like so; both worked this one is just now not modifying the original state.
case BROWSER: { let browser = {...state}
browser.graphSite.browsable[action.id] = action.payload
return {...state,
graphSite: {...state.graphSite,
graphBrowser: {...state.graphSite.graphBrowser},
browsable: {...browser.graphSite.browsable}
}
}
}
Destructuring works great here
case BROWSER: { let {graphSite: {browsable: browser}} = state
browser[action.id] = action.payload
return {...state,
graphSite: {...state.graphSite,
graphBrowser: {...state.graphSite.graphBrowser},
browsable: {...browser}
}
}
}
I also realize I could use something like deepFreeze or immutable to check for these but practicing.