Vuex reactive mapGetters with arguments passed through - vue.js

I have lots of getters that pass arguments to the store such as:
this.$store.getters['getSomeThing'](this.id)
And I'm not finding recommendations for how to optimally use mapGetters to maintain reactivity, while passing the arguments through. One suggestion I found was to map the getter and then pass the argument in mounted:
computed: {
...mapGetters([
'getSomeThing'
])
},
mounted () {
this.getSomeThing(this.id)
}
This really seems to be sub-optimal, as it would only check for a change to state on mounted. Any suggestions for how to best maintain reactivity while passing an argument to a getter? Here's an example of a getter that would match the above code:
getSomeThing: (state) => (id) => {
return state.things.find(t => { return t.id === id })
}

Here is a snippet from a project I have:
computed: {
...mapGetters('crm', ['accountWithId']),
account() {
return this.accountWithId(this.$route.params.id)
}
},
This makes this.account reactive and dependent on the param.
So...
computed: {
...mapGetters([
'getSomeThing'
]),
thing() {
return this.getSomeThing(this.id)
}
},

Related

Computed property “eleron” was assigned to but it has no setter

How to fix it?
computed: {
...mapGetters({
eleron: 'promoter/eleron',
}),
},
GetInfo (call when press search button):
getInfo() {
this.loading = true;
axios.post('/srt', {
search: this.search
})
.then((response) => {this.eleron = response.data, console.log(response.data), this.loading = false;});
},
You are mapping the getters from vuex. This means that you can only get the value from the store, you cannot write to it.
You need to also map a mutation.
Something like this should work, depending on the fact that you have a mutation defined on the store:
methods: {
...mapMutations([
'updateEleron'
]),
}
And then call it in the promise callback
this.updateEleron(response.data)
Note: vuex offers read only access to variables from outside the store. Writing to a variable needs to be done from inside a mutation or action.

Trying to access a state object in mounted() in my Vue component

I have a Vuex state that holds a user ID. In my component's mounted(), I try to use that user ID, but it's always null.
How do I get the state from my computed mapGetters into my mounted()?
Here's my computed:
computed: {
...mapGetters('auth', [
'userid'
])
}
And here's my mounted():
mounted () {
HTTP.get('account/' + this.userid + '/')
.then((response) => {
this.account = response.data
})
}
The this.userid is always null.
BTW when I look at the Vue inspector, the auth/userid has the correct value in the getter auth/userid. How do I access auth.userid from mounted()?
userid might not be available at the time component is mounted. You can fix it by watching userid value, and only call HTTP request when userid is changed and available:
computed: {
...mapGetters('auth', [
'userid'
])
},
watch: {
'userid': {
handler (newVal) {
if (newVal) { // check if userid is available
this.getAccountInformation()
}
},
immediate: true // make this watch function is called when component created
}
},
methods: {
getAccountInformation () {
HTTP.get('account/' + this.userid + '/')
.then((response) => {
this.account = response.data
})
}
}
DEBUG
To debug this, first skip the mapGetters, and even getters, and return your state directly.
For example.
computed:{
userId() { return this.$store.state.auth.userid }
}
I don't know how your store or modules are set up, so you might have to change things a bit.
Once that works, add it to your getters and use this.$store.getters.userid, or such.
Finally, when that works, try your original mapGetters and double check your module alias.
POSSIBLE ASYNC ISSUE
Now, on the other hand, if your getter is async, you will also get a null, before the userid promise resolves. You would have to use an asyncComputed, or wait for the result in your mounted.

vuejs 2 how to watch store values from vuex when params are used

How can I watch for store values changes when params are used? I normally would do that via a getter, but my getter accepts a param which makes it tricky as I've failed to find documentation on this scenario or a stack Q/A.
(code is minimized for demo reasons)
My store.js :
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
let report = {
results: [],
};
export const store = new Vuex.Store({
state: {
broken: Object.assign({}, report),
},
results: (state) => (scan) => {
return state[scan].results
},
});
vue-component.vue :
computed: {
...mapGetters([
'results',
]),
watch: {
results(){ // How to pass the param ??
// my callback
}
So basically I would like to find out how to pass the param so my watch would work.
In my opinion, there is no direct solution for your question.
At first, for watch function, it only accept two parameters, newValue and oldValue, so there is no way to pass your scan parameter.
Also, your results property in computed, just return a function, if you watch the function, it will never be triggered.
I suggest you just change the getters from nested function to simple function.
But if you really want to do in this way, you should create a bridge computed properties
computed: {
...mapGetters([
'results',
]),
scan() {
},
mutatedResults() {
return this.results(this.scan);
},
watch: {
mutatedResults() {
}
}
}

mapState with setter

I would like to assign setter methods via mapState. I currently use a workaround where I name the variable that I am interested in (todo) as a temporary name (storetodo) and then refer to it in another computed variable todo.
methods: {
...mapMutations([
'clearTodo',
'updateTodo'
])
},
computed: {
...mapState({
storetodo: state => state.todos.todo
}),
todo: {
get () { return this.storetodo},
set (value) { this.updateTodo(value) }
}
}
I would like to skip the extra step and define the getter, setter directly within mapState.
Why would I want to do this?
The normal approach would be use mapMutations/mapActions & mapState/mapGetters
without the computed get/set combination that I have illustrated above and to reference the mutation directly in the HTML:
<input v-model='todo' v-on:keyup.stop='updateTodo($event.target.value)' />
The getter/setter version allows me to simply write:
<input v-model='todo' />
You can't use a getter/setter format in the mapState
what you can try is directly return the state in your get() and remove mapState from the computed property
computed: {
todo: {
get () { return this.$store.state.todos.todo},
set (value) { this.updateTodo(value) }
}
}
Here is a related but not same JsFiddle example
This is my current workaround. Copied from my personal working project
// in some utils/vuex.js file
export const mapSetter = (state, setters = {}) => (
Object.keys(state).reduce((acc, stateName) => {
acc[stateName] = {
get: state[stateName],
};
// check if setter exists
if (setters[stateName]) {
acc[stateName].set = setters[stateName];
}
return acc;
}, {})
);
In your component.vue file
import { mapSetter } from 'path/to/utils/vuex.js';
export default {
name: 'ComponentName',
computed: {
...mapSetter(
mapState({
result: ({ ITEMS }) => ITEMS.result,
total: ({ ITEMS }) => ITEMS.total,
current: ({ ITEMS }) => ITEMS.page,
limit: ({ ITEMS }) => ITEMS.limit,
}),
{
limit(payload) {
this.$store.dispatch({ type: TYPES.SET_LIMIT, payload });
},
},
)
},
}
now you can use the v-model bindings. l
Another way of approaching that is using store mutations like below:
//in your component js file:
this.$store.commit('setStoretodo', storetodo)
Assuming you define setStoretodo in mutations of your vuex store instance (which is something recommended to have anyways):
//in your vuex store js file:
state:{...},
actions: {...}
...
mutations: {
setStoretodo(state, val){
state.storetodo = val
},
...
}
...
That keeps the property reactive as mapState will grab the updated value and it will be rendered automatically.
Surely, that's not as cool as just writing this.storetodo = newValue, but maybe someone will find that helpful as well.

Pass params to mapGetters

I use vuex and mapGetters helper in my component. I got this function:
getProductGroup(productIndex) {
return this.$store.getters['products/findProductGroup'](productIndex)
}
Is it possible to move this somehow to mapGetters? The problem is that I also pass an argument to the function, so I couldn't find a way to put this in mapGetters
If your getter takes in a parameter like this:
getters: {
foo(state) {
return (bar) => {
return bar;
}
}
}
Then you can map the getter directly:
computed: {
...mapGetters(['foo'])
}
And just pass in the parameter to this.foo:
mounted() {
console.log(this.foo('hello')); // logs "hello"
}
Sorry, I'm with #Golinmarq on this one.
For anyone looking for a solution to this where you don't need to execute your computed properties in your template you wont get it out of the box.
https://github.com/vuejs/vuex/blob/dev/src/helpers.js#L64
Here's a little snippet I've used to curry the mappedGetters with additional arguments. This presumes your getter returns a function that takes your additional arguments but you could quite easily retrofit it so the getter takes both the state and the additional arguments.
import Vue from "vue";
import Vuex, { mapGetters } from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
myModule: {
state: {
items: [],
},
actions: {
getItem: state => index => state.items[index]
}
},
}
});
const curryMapGetters = args => (namespace, getters) =>
Object.entries(mapGetters(namespace, getters)).reduce(
(acc, [getter, fn]) => ({
...acc,
[getter]: state =>
fn.call(state)(...(Array.isArray(args) ? args : [args]))
}),
{}
);
export default {
store,
name: 'example',
computed: {
...curryMapGetters(0)('myModule', ["getItem"])
}
};
Gist is here https://gist.github.com/stwilz/8bcba580cc5b927d7993cddb5dfb4cb1