Understanding State and Getters in Nuxt.js: Getters won't working - vue.js

i'm new to Vue and Nuxt and i'm building my first website in Universal mode with these framework.
I'm a bit confused on how the store works in nuxt, since following the official documentation i can't achieve what i have in mind.
In my store folder i have placed for now only one file called "products.js", in there i export the state like this:
export const state = () => ({
mistica: {
id: 1,
name: 'mistica'
}
})
(The object is simplified in order to provide a cleaner explanation)
In the same file i set up a simple getter, for example:
export const getters = () => ({
getName: (state) => {
return state.mistica.name
}
})
Now, according to the documentation, in the component i set up like this:
computed: {
getName () {
return this.$store.getters['products/getName']
}
}
or either (don't know what to use):
computed: {
getName () {
return this.$store.getters.products.getName
}
}
but when using "getName" in template is "undefined", in the latter case the app is broken and it says "Cannot read property 'getName' of undefined"
Note that in the template i can access directly the state value with "$store.state.products.mistica.name" with no problems, why so?
What am i doing wrong, or better, what didn't i understand?

Using factory function for a state is a nuxt.js feature. It is used in the SSR mode to create a new state for each client. But for getters it doesn't make sense, because these are pure functions of the state. getters should be a plain object:
export const getters = {
getName: (state) => {
return state.mistica.name
}
}
After this change getters should work.
Then you can use the this.$store.getters['products/getName'] in your components.
You can't use this.$store.getters.products.getName, as this is the incorrect syntax.
But to get simpler and more clean code, you can use the mapGetters helper from the vuex:
import { mapGetters } from "vuex";
...
computed: {
...mapGetters("products", [
"getName",
// Here you can import other getters from the products.js
])
}

Couple of things. In your "store" folder you might need an index.js for nuxt to set a root module. This is the only module you can use nuxtServerInit in also and that can be very handy.
In your products.js you are part of the way there. Your state should be exported as a function but actions, mutations and getters are just objects. So change your getters to this:
export const getters = {
getName: state => {
return state.mistica.name
}
}
Then your second computed should get the getter. I usually prefer to use "mapGetters" which you can implement in a page/component like this:
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters({
getName: 'products/getName'
})
}
</script>
Then you can use getName in your template with {{ getName }} or in your script with this.getName.

Related

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????

Vuex: createNamespacedHelpers with dynamic namespace

In almost all guides, tutorial, posts, etc that I have seen on vuex module registration, if the module is registered by the component the createNamespacedHelpers are imported and defined prior to the export default component statement, e.g.:
import {createNamespacedHelpers} from 'vuex'
const {mapState} = createNamespacedHelpers('mymod')
import module from '#/store/modules/mymod'
export default {
beforeCreated() {
this.$store.registerModule('mymod', module)
}
}
this works as expected, but what if we want the module to have a unique or user defined namespace?
import {createNamespacedHelpers} from 'vuex'
import module from '#/store/modules/mymod'
export default {
props: { namespace: 'mymod' },
beforeCreated() {
const ns = this.$options.propData.namespace
this.$store.registerModule(ns, module)
const {mapState} = createNamespacedHelpers(ns)
this.$options.computed = {
...mapState(['testVar'])
}
}
}
I thought this would work, but it doesnt.
Why is something like this needed?
because
export default {
...
computed: {
...mapState(this.namespace, ['testVar']),
...
},
...
}
doesnt work
This style of work around by utilising beforeCreate to access the variables you want should work, I did this from the props passed into your component instance:
import { createNamespacedHelpers } from "vuex";
import module from '#/store/modules/mymod';
export default {
name: "someComponent",
props: ['namespace'],
beforeCreate() {
let namespace = this.$options.propsData.namespace;
const { mapActions, mapState } = createNamespacedHelpers(namespace);
// register your module first
this.$store.registerModule(namespace, module);
// now that createNamespacedHelpers can use props we can now use neater mapping
this.$options.computed = {
...mapState({
name: state => state.name,
description: state => state.description
}),
// because we use spread operator above we can still add component specifics
aFunctionComputed(){ return this.name + "functions";},
anArrowComputed: () => `${this.name}arrows`,
};
// set up your method bindings via the $options variable
this.$options.methods = {
...mapActions(["initialiseModuleData"])
};
},
created() {
// call your actions passing your payloads in the first param if you need
this.initialiseModuleData({ id: 123, name: "Tom" });
}
}
I personally use a helper function in the module I'm importing to get a namespace, so if I hadmy module storing projects and passed a projectId of 123 to my component/page using router and/or props it would look like this:
import projectModule from '#/store/project.module';
export default{
props['projectId'], // eg. 123
...
beforeCreate() {
// dynamic namespace built using whatever module you want:
let namespace = projectModule.buildNamespace(this.$options.propsData.projectId); // 'project:123'
// ... everything else as above
}
}
Hope you find this useful.
All posted answers are just workarounds leading to a code that feels verbose and way away from standard code people are used to when dealing with stores.
So I just wanted to let everyone know that brophdawg11 (one of the commenters on the issue #863) created (and open sourced) set of mapInstanceXXX helpers aiming to solve this issue.
There is also series of 3 blog posts explaining reasons behind. Good read...
I found this from veux github issue, it seems to meet your needs
https://github.com/vuejs/vuex/issues/863#issuecomment-329510765
{
props: ['namespace'],
computed: mapState({
state (state) {
return state[this.namespace]
},
someGetter (state, getters) {
return getters[this.namespace + '/someGetter']
}
}),
methods: {
...mapActions({
someAction (dispatch, payload) {
return dispatch(this.namespace + '/someAction', payload)
}
}),
...mapMutations({
someMutation (commit, payload) {
return commit(this.namespace + '/someMutation', payload)
})
})
}
}
... or maybe we don't need mapXXX helpers,
mentioned by this comment https://github.com/vuejs/vuex/issues/863#issuecomment-439039257
computed: {
state () {
return this.$store.state[this.namespace]
},
someGetter () {
return this.$store.getters[this.namespace + '/someGetter']
}
},

How to get state value and pass in methods parameters

how can i use vuex state and use to methods property of components?
I have a state "currentThreadId" but i want to use that state in methods?
what i struggle is i can get the "currentThreadId" in my vuex state using computer property. but i don't know how to pass it to methods. i want to use that id for axios request.
messagesServices.attachEstimate({message_id}).then(response => {
console.log(response);
});
all i want is the way how to get the id and use to methods property.
As described here there are multiple ways to access state in your components. I'd suggest using the mapState helper with the spread operator. For example:
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState([
'currentThreadId',
]),
},
methods: {
someMethod() {
console.log(this.currentThreadId)
},
},
};
</script>

Vuex computed properties only work with getters

When I put this in my Vue component ...
// using store getter
computed: {
authenticated() {
return this.$store.getters.authenticated
}
}
... it works. The value for authenticated is reactive and the computed property returns true when the value in the vuex store is true.
This should work ... (and would be the right way according to the docs)
// using store state
computed: {
authenticated() {
return this.$store.state.authenticated
}
}
... but doesn't. The computed property is always false.
It doesn't even work on initial state, so I guess it has nothing to do with the action or mutation. The vuex store holds the correct values in the state and the getters (Firefox Vue DevTools).
My store looks like this:
const state = {
authenticated: authenticate.isAuthenticated(),
};
const getters = {
authenticated () {
return state.authenticated
}
};
const mutations = {
isAuthenticated (state, isAuthenticated) {
state.authenticated = isAuthenticated
}
};
So, it works with store getters but not with store state. Afaik the store state should be reactive as well.
Any idea what I might be doing wrong?
More as an aside to this discussion, vuex offers the mapGetters, mapState, mapActions, and mapMutations helper functions.
In the case of the authenticated getter, you would map it like:
import { mapGetters } from 'vuex
computed: {
...mapGetters({
authenticated: 'authenticated'
})
}
Helps to keep your code clean and concise, imo.
Assuming you construct your Vuex.Store as I do below, the computed works as expected using either state.authenticated or getters.authenticated.
The mutations section made no difference, so I took it out to make things minimal.
As Bert noted, your getter should take state as a parameter; otherwise, it is using the declared const, which is the same thing in this case, but deceptive to read.
const authenticate = {
isAuthenticated() {
return true;
}
};
const state = {
authenticated: authenticate.isAuthenticated()
};
const getters = {
authenticated (state) {
return state.authenticated;
}
};
const store = new Vuex.Store({
state,
getters
});
new Vue({
el: '#app',
store,
computed: {
authenticated() {
return this.$store.state.authenticated;
}
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<script src="//unpkg.com/vuex#latest/dist/vuex.js"></script>
<div id="app">
Anything? {{authenticated}}
</div>
const state = {
authenticated: authenticate.isAuthenticated(),
};
The state is an object. An attribute in the object is trying to call the result of a function. This might be the problem, as it would be asking an object to invoke functions. Try setting it to a fixed value first, and change the state value by invoking a mutation when needed.
You could also try js object function call to invoke the authenticate.isAuthenticated() function inside the state object.
Details here: https://www.w3schools.com/js/js_function_call.asp
Possible solution:
const state = {
authenticated: function(){ return authenticate.isAuthenticated() },
};
I do not think the problem is with using getters or state. Since state ran correctly, getters should do the same since it is pointing to state. Have you exported getters from your store? That seems to be the likely issue. As previously mentioned, you ought to pass state as a parameter when using vuex getters

How to use shorter path to get vuex contents?

In my vuex /store/state.js I have an export default that looks like this:
export default {
foo: {
bar:{
tar: {
info: 42
}
}
}
}
So, whenever I want to access info, I usually do in my methods like this;
methods: {
getInfo () {
return this.$store.state.foo.bar.tar.info
}
}
This is just for a demo purpose, and mine is actually a bit worse, but I ended up doing the same so, I tried minimize the code using a computed prop:
computed: {
info () {
return this.$store.state.foo.bar.tar.info
}
}
Now, I just call info but still, not sure if there is a better way to get values, because sometimes I just need to call info only one in a page, so I have to use the full path or create a computed property for it.
Is there any other way to do this
I always separate vuex into separated modules. For instance if you have store for foo module. I will create file named foo.js which contains
const fooModule = {
state: {
foo: {
bar: {
tar: {
info: 42
}
}
}
},
getters: {
info (state) {
return state.foo.bar.tar.info
}
},
mutations: {
setInfo (state, payload) {
state.foo.bar.tar.info = payload
}
},
actions: {
getInfo ({commit}, payload) {
commit('setInfo', payload)
}
}
}
export default fooModule
Then in your main index vuex, import the module like this way
import Vue from 'vue'
import Vuex from 'vuex'
import fooModule from './foo.js'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
fooModule
}
})
export default store
Then if you wanna get info, you just write your code like this
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters([
'getInfo'
])
}
}
#jefry Dewangga has the the right idea but introducing mapGetters is unnecessary.
VueX by default includes namespacing for modules which allows us to load multiple modules into a store and then reference them with or without namespacing them and it'll figure the rest out.
For Instance if we have the structure of
|-- store
|-- Modules
|-- module1.js
|-- module2.js
|-- module3.js
|-- index.js
We can use index in such a way to bind all of our modules into our Vuex store doing the following:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules
})
An example of our module1 could be:
const state = {
LoggedIn: true
}
const mutations = {
LOGIN(state) {
state.LoggedIn = true;
},
LOGOFF(state) {
state.LoggedIn = false;
}
}
const actions = {}
export default {
state,
mutations,
actions
}
This in turn gives us the ability to say:
this.$store.commit('LOGIN');
Note that we haven't used any namespacing but as we haven't included any duplicate mutations from within our modules were absolutely fine to implicitly declare this.
Now if we want to use namespacing we can do the following which will explicitly use out module:
this.$store.module1.commit('LOGIN');
MapGetters are useful but they provide a lot of extra overhead when we can neatly digest out modules without having to continuously map everything, unless well we find the mapping useful. A great example of when MapGetters become handy is when we are working many components down in a large project and we want to be able to look at our source without having to necessarily worry about the frontend implementation.