mapGetters function behaves differently from store.getters - vue.js

as you can see below I have a getter which returns a new method which takes a parameter, according to the documentation I should be able to call this method without doing two method calls. This works great if I use the store.getters directly, however I want to use mapGetters, but in order to get a value and not just the returned function. I need to call the method 'twice' as you would expect in vanilla js. Am I doing something wrong or is this an edge case?
Thanks
export default {
components: { AddRemove, NumberInput },
methods: {
...mapGetters({getExtra: types.GET_EXTRA}),
// Why can I omit the parenthesis when using the store.getters directly?
extraAmountWithoutParenthesis(code) {
return this.$store.getters.getExtra(code) // returns integer
},
// And why do I require them when using a named mapGetters method
extraAmountRequiresParenthesis(code) {
// return this.getExtra(code) // returns function
return this.getExtra()(code) // returns integer
}
}
}
In my getters:
[types.GET_EXTRA]: (state) => (code) => {
let value = state.extras[code]
if (!value) {
value = 0
}
return parseInt(value)
},

mapGetters() result must be merged into computed, not methods.

Related

Difference between watch and $watch

Just a simple question.
What is the difference between options and instance methods?
Based on the watch example, we can implement watcher as an option (https://v3.vuejs.org/api/options-data.html#watch) and a method of an instance (https://v3.vuejs.org/api/instance-methods.html#watch).
From my point of understanding, I can implement exactly the same feature with both methods and the only differences would be the syntax and the place of implementation.
If I am mistaken, can somebody explain to me based on example the difference between these two?
You are indeed (almost) correct with your assumption.
There is 2 major advantage of this.$watch() though.
You can start watching dynamically
the return-value of this.$watch() is an unwatch function with which you can dynamically stop the watcher during runtime
But that doesn't necessarly mean that you should always use this.$watch() over watch: {}. The opposite. You should always think about what your use case needs
Unwatch-example:
export default {
//..
created(props) {
const unwatchAge = this.$watch(() => this.user.age, (value, oldValue) => {
if (value >= 18) {
alert('You are now allowed to drive a car!');
unwatchAge(); //we don't need age watching for anything else
}
});
}
//...
}
BTW with VUE3 you might wanna look into the watch() / watchEffect() composition API methods.
watch() does the same as watch: {} and this.$watch() and also has an unwatch-method as return-value.
watchEffect() checks any value mentioned inside parameter (function) and puts a watcher on it internally.
watch() Example (composition)
import { toRef, watch} from 'vue';
export default {
//...
setup(props) {
const age = toRef(props.age);
const unwatchAge = watch(age, console.log);
// no () => age or () => age.value needed as age is a reference by using toRef and references can be handles like this
setTimeout(() => {
console.warn('unwatching age!');
unwatchAge();
}, 5000);
}
//...
}
watchEffect() Example (composition)
import { toRef, watchEffect} from 'vue';
export default {
//...
setup(props) {
const age = toRef(props.age);
watchEffect(() => {
if (age.value >= 18) {
alert('You are now allowed to drive a car!');
}
});
//vue will internally recognize that age has to be watched here. No telling it manually.
}
//...
}
The main difference from the docs it is that the instance method returns a unwatchable that you can trigger to stop watching a certain property:
const unwatchUsers = this.$watch('users', () => {});
setTimeout(unwatchUsers, 1000);
This is not possible with options API. It is extremely useful to use this unwatch returned by this.$watch when something happens in your app.
Have in mind what is the most appropriate to your use case and use it accordingly

How to call a getter inside a computed properties

I am using the modules mode of the store and in my projects.js inside my store folder I have:
export const getters = {
loadedProjects(state) {
return state.loadedProjects;
}
}
now in my computed how should I call it?
I’m trying like that:
computed: {
loadedProjects() {
return this.$store.getters.projects.loadedProjects;
},
}
but I get this error:
Cannot read property ‘loadedProjects’ of undefined
I had the same problem, if you are using the modules mode you can call your getters like that (in your case): this.$store.getters['projects/loadedProjects'];
So try to change your computed like that:
computed: {
loadedProjects() {
return this.$store.getters['projects/loadedProjects'];
},
}
You have to call your getter like this:
loadedProjects() {
return this.$store.getters['projects/loadedProjects'];
}
$store.getters['moduleName/getterName']

Returning a getters in a computed create a loop

I am calling inside the computed an action from the store to run it and after I am returning a getter, this will create a loop.
The HTML
{{loadedProjects}}
The computed
computed: {
loadedProjects() {
this.$store.dispatch("getProjects");
return this.$store.getters.loadedProjects;
}
}
The store
import Vuex from "vuex";
import axios from "axios";
const createStore = () => {
return new Vuex.Store({
state: {
loadedProjects: []
},
mutations: {
setProjects(state, projects) {
state.loadedProjects = projects
}
},
actions: {
getProjects(vuexContext) {
console.log("hello1")
return axios.get("THE API URL")
.then(res => {
console.log("hello2")
vuexContext.commit("setProjects", res.data);
})
.catch(e => console.log(e));
}
},
getters: {
loadedProjects(state) {
return state.loadedProjects;
}
}
});
};
export default createStore;
I expect to call my action to populate my state and after to return my state to render my data.
What is the point of using the store action that makes an API call inside the computed property ... maybe you want to trigger loadedProjects change ? ....computed property is not asynchronous so either way the return line will be executed before the you get the response... you might try vue-async-computed plugin OR just use the call on the created hook like you have done which is the better way and you don't have to use a computed property you can just {{ $store.getters.loadedProjects }} on your template
Computed properties should not have side effects (e.g. calling a store action, changing data, and so on). Otherwise it can happen that the triggered side effect could lead to a re-rendering of the component and possible re-fetching of the computed property. Thus, an infinite loop
I changed the code like that:
created: function () {
this.$store.dispatch("getProjects")
},
computed: {
loadedProjects() {
return this.$store.getters.loadedProjects
}
}
It is working now but I would like to know but I have that problem working inside the computed and also I wonder if it's the best solution. Any help????

How to pass variable from Vuex store to a function as value?

So I am working with what would appear to be a simple issue, but it is eluding me this evening. I have a value that is set in a Vuex store. In my component file, I declare a constant where the value is retrieved from the store. Everything up to this point works perfectly.
Then, upon submitting a form in the component a script function is run. Within that function, I need to pass the value from the Vuex store along with a couple of other arguments to another function. The function gets call, the arguments are passed, and it all works as expected.
However ... I am getting console errors stating ...
Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers.
What is the correct what to retrieve a value from the Vuex store and then pass that value to a function?
Some more detail here ... Page 1 stores an object representing a CognitoUser in the store using a mutation function which works as expected, then transitions to Page 2. Page 2 retrieves the object from the store (tried both the data and computed methods mentioned below as well as using the getter directly in the code - all fail the same). Within a method on Page 2, the object from the store is accessible. However, that method attempts to call the Amplify completeNewPassword method, passing the CongnitoUser object as an argument. This is the point that the error appears stating that the mutation handler should be used even though there is no change to the object on my end.
....
computed: {
user: {
get(){
return this.$store.getters[ 'security/localUser' ]
},
set( value ){
this.$store.commit( 'security/setLocalUser', value )
}
}
},
....
methods: {
async submitForm(){
this.$Amplify.Auth.completeNewPassword( this.user, this.model.password, this.requiredAttributes )
.then( data => {
....
This is almost certainly a duplicate question. You can refer to my answer here.
Basically you should pass the Vuex value to a local data item and use that in your component function. Something like this.
<script>
export default {
data: () => ({
localDataItem: this.$store.getters.vuexItem,
})
methods: {
doSomething() {
use this.localDataItem.here
}
}
}
</script>
The canonical way of handling this by using computed properties. You define a computed property with getter and setter and proxy access to vuex thru it.
computed: {
localProperty: {
get: function () {
return this.$store.getters.data
},
set: function (val) {
this.$store.commit(“mutationName”, val )
}
}
}
Now you can use localProperty just as we use any other property defined on data. And all the changes get propagated thru the store.
Try if this work
<template>
<div>
<input :value="user" #change="onChangeUser($event.target.value)"></input>
</div>
</template>
<script>
computed: {
user() {
return this.$store.getters[ 'security/localUser' ]
}
},
methods: {
onChangeUser(user) {
this.$store.commit( 'security/setLocalUser', user );
},
async submitForm(){
this.$Amplify.Auth.completeNewPassword( this.user, this.model.password, this.requiredAttributes )
.then( data => {
...
}
</script>

Passing values between exported and non-exported part of script

How to pass values between exported part of a script and non-exported one?
The construction looks like this:
<script>
// PART 1:
import { EventBus } from './event-bus.js';
EventBus.$on('value-received', value => {
this.receivedValue = value;
});
// PART 2:
export default {
data() {
return {
receivedValue: ''
}
},
watch: {...},
methods: {...}
}
</script>
How can I get the value assigned to receivedValue variable and made usable by Vue methods?
Because your EventBus.$on call uses an arrow function, the this it references is the this in scope at the time of the EventBus call.
If you are okay with all instances of your event having the same receivedValue, you could redirect the values received to an object in the scope of your file:
var shared = { receivedValue: '' };
EventBus.$on('value-received', value => {
shared.receivedValue = value;
});
export default {
data() { return shared; }
watch: ...,
methods: ....
}
Vue.js will register a handler to react on changes to the object you returned, ie. shared.
If for some reason you want a separate event stream per instance of your component, you need to create a new object inside the data() function and have your event bus update the new object directly.