I have a problem using rootGetters in Nuxt.js - vue.js

I think it's a problem with rootGetters or data types.
// sheet.js
// character = Object
// number = 100
export const getters = {
getNumber: state => {
return Number(state.character.number); // its return 100
}
};
and called getNumber to preview.js.
// preview.js
export const state = () => ({
dummy: 0
});
export const getters = {
numberIs: (state, rootGetters) => {
return Math.round(state.dummy + rootGetters["sheet/getNumber"]); // undefined
}
};
and numberIs return undefined.
What did I miss?

The order of the parameters matters. Vuex getter signature is (state, getters, rootState, rootGetters), so currently what you think is rootGetters is actually just getters.
https://vuex.vuejs.org/guide/modules.html#accessing-global-assets-in-namespaced-modules
It's a little bit deceptive because of how actions pass in the context-object, where you can pick and choose what you want to use. Here you must use 4 parameters to get to rootGetters. (Or parse it out from arguments)
numberIs: (state, _whatever, _idontcare, rootGetters) => {
return Math.round(state.dummy + rootGetters["sheet/getNumber"]);
}

Related

Nuxt store getter not working, ID given to payload is not an Integer + Error: [vuex] do not mutate vuex store state outside mutation handlers

I am trying to make a product detail page. The detail page is named _id.
When opened the id is replaced with the product id. On opening the page the state is set with data fetched from an api.
After that i am trying to use a computed property that refers to a getter named getProduct() with an id (this.$route.params.id) in the payload.
This is how my _id.vue looks like:
methods: {
...mapActions("products", ["fetchProducts",]),
...mapGetters("products", ["getProduct",]),
},
async mounted() {
this.fetchProducts()
},
computed: {
product() {
return this.getProduct(this.$route.params.id)
}
}
This is how my store file named products.js looks like:
import axios from "axios"
export const state = () => ({
producten: []
})
export const mutations = {
setProducts(state, data) {
state.producten = data
}
}
export const getters = {
getProduct(state, id) {
console.log(id)
return state.producten.filter(product => product.id = id)
}
}
export const actions = {
async fetchProducts({ commit }) {
await axios.get('/api/products')
.then(res => {
var data = res.data
commit('setProducts', data)
})
.catch(err => console.log(err));
}
}
What works is creating the state, but when i try to use the getter something goes wrong.
As you can see i console.log() the id given to it. Which logs the following:
I also get the error: client.js?06a0:103 Error: [vuex] do not mutate vuex store state outside mutation handlers.
Which I'm not doing as far as I know?
**Note: **these errors get logged as much as the length of my state array is.
From the Vuex documentation:
Vuex allows us to define "getters" in the store. You can think of them as computed properties for stores. Like computed properties, a getter's result is cached based on its dependencies, and will only re-evaluate when some of its dependencies have changed.
Like computed, getters does not support having arguments.
But there is a way to have "method-style access" to a getter: https://vuex.vuejs.org/guide/getters.html#property-style-access
You can also pass arguments to getters by returning a function. This is particularly useful when you want to query an array in the store:
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
Note that getters accessed via methods will run each time you call them, and the result is not cached.

How to update API path dynamically in VUEX state

I am trying to dynamically update the API path in my Vuex state. Vuex must have a default path "example.com/api/datasetA.json" set when the page loaded and I want to update the path to "example.com/api/datasetB.json" by the user interaction and fetch the new API data immediately.
The relevant part of my code is as follows (updated code):
VUEX:
export const state = () => ({
apiData: [],
apiId: 'datasetA.json'
});
export const mutations = {
fillApiData: (state, data) => {state.apiData = data},
updateApi: (state, newApiId) => {state.apiId = newApiId;}
};
export const actions = {
async getApiData({commit, state}) {
const response = await this.$axios.$get('https://example/api/'+state.apiId);
commit('fillApiData', response);
then VUE method as follows:
methods: {
updateApi(apiId) {
this.$store.commit('updateApi', apiId)
}
Create a mutation that changes the vuex state. Then run this mutation(commit) in the getApiData function
export const state = () => ({
apiData: [],
apiId: 'datasetA.json'
});
export const mutations = {
updateAPI(state, newApiId ) {
state.apiId = newApiId;
}
};
export const actions = {
async getApiData({commit, state}) {
const response = await this.$axios.$get('https://example/api/'+state.apiId);
commit('updateValue', response);
commit('updateAPI', 'some.new.datasetB.json');
}
}
I can update the state directly by using this.$store.state.apiId = apiId in methods but I know this is bad practice
You are correct. However, if you would like that approach to update the state outside Vuex, you can use mutations to change the Vuex - This is good practice.
Then you can do
this.$store.commit('updateAPI', 'my new value')

Vuex separate file getters.js and getter with argument

I have separate file getters.js:
export const getDate = (state, format) => {
const getDateFormat = (state, format) => {
return moment(state.date).format(format)
}
return getDateFormat(state, format)
}
I using this method getDate inside component:
computed:{
...mapGetters({
getDate: 'getDate',
}),
getDateFormat() {
return this.getDate('dd-mm-yyyy')
},
},
but Vue return me error:
this.getDate is not a function
You can't pass custom arguments to getter. The arguments of getter:
state, getters, rootState
getItem(state, getters, rootState) {
}
OK I fix it, I change my method to arrow function:
export const getDate = state => format => {
//...
}

Can I dispatch vuex action inside getter function?

I want to dispatch action inside getter function.
1. Is it possible and right.
2. If yes how can I do it?
I guess it will be something like this dispatch('GET_BOOKS');
const getters = {
getAllBooksDispatch: (state, getters, dispatch) => {
if (state.books === null) {
dispatch('GET_BOOKS');
}
return state.books
},
};
But it does not work.
So my store file looks like this.
const initialState = {
books: null
};
const getters = {
getAllBooksDispatch: (state, getters, dispatch) => {
if (state.books === null) {
dispatch('GET_BOOKS');
}
return state.books
},
};
const mutations = {
SET_BOOKS: (state,{data}) => {
console.log('SET_BOOKS mutations')
state.books = data;
},
};
const actions = {
GET_BOOKS: async ({ commit }) => {
let token = users.getters.getToken;
let query = new Promise((resolve, reject) => {
axios.get(config.api + 'books', {token}).then(({data}) => {
if (data) {
commit('SET_BOOKS', {data: data})
resolve()
} else {
reject(data.message);
}
}).catch(() => {
reject('Error sending request to server!');
})
})
},
};
No, you can't. At least not the way you want to. The third argument in a getter is the rootState object when using modules, not dispatch. Even if you find a way to dispatch an action inside a getter it won't work the way you expect. Getters must be synchronous, but actions can be (and in this example are) asynchronous. In your example, GET_BOOKS would be dispatched but the getter would still return state.books as null.
I'd recommend handling this sort of lazy-loading outside of the Vuex store.

Vuex state module structure

I adopted the file structure on vuex with modules. Originally I just had everything in one store file (I don't know what I was thinking). Now that I refactored the code to a better more maintainable structure I am having issues with how to mimic the state that I had before.
My previous state for the user field was just a user object like this:
user: {...}
Now that I used this format
const state = {
}
const mutations = {
fetchUser(state,user){
console.log(user)
state = user;
}
};
const actions = {
currentUser: ({commit}) => {
axios.get('/user').then(response => {
if(response.status == 200){
commit('fetchUser', response.data.data);
}
}).catch(response => {
});
}
}
My state translates to :
user:{}
with an empty object. Shouldn't this assign the user into that user state object or am I missing something.
From docs:
Inside a module's mutations and getters, the first argument received will be the module's local state.
So your mutation should access the module object:
const mutations = {
setUser(state, user){
state.user = user; // Assuming you have a user module **
}
};
** Assuming you have a user module this way:
const store = new Vuex.Store({
modules: {
user: moduleUser,
// more modules ...
}
})
Mutations should only modified the state. You should change your logic to fetch user data from an action only and not from your mutation itself, for example:
const actions = {
currentUser: ({commit}) => {
axios.get('/user').then(response => {
if(response.status == 200){
var response = response.data.data;
commit('setUser', response);
}
}).catch(response => {
});
}
}