Nuxt Vuex mutation runs but doesn't update state - vue.js

I'm trying to get some data by nuxtServerInit and save it in state
store/index.js
import { fireDb } from '~/plugins/firebase'
export const state = () => ({
posts: []
})
export const mutations = {
addPosts (state, post) {
state.posts.push(post)
console.log('mutation =>', state.posts.length)
}
}
export const actions = {
nuxtServerInit (state, ctx) {
fireDb.collection('posts').orderBy('timestamp', 'desc').limit(3).get().then((snapshot) => {
snapshot.forEach((doc) => {
state.commit('addPosts', doc.data())
})
console.log('action => ', state.posts.length)
})
}
}
when I run this code console output is
mutation => 1
mutation => 2
mutation => 3
ERROR Cannot read property 'length' of undefined
And vue dev tools also doesn't show there's data inside posts[].
What am I missing here?

It looks like nuxtServerInit is dispatched as an action with the Nuxt context. Being an action, the first argument will be the Vuex context.
The Vuex context exposes several properties including state and commit.
The docs also say:
Note: Asynchronous nuxtServerInit actions must return a Promise or leverage async/await to allow the nuxt server to wait on them.
You can change your code to:
async nuxtServerInit({state, commit}, ctx) {
let snapshot = await fireDb.collection('posts').orderBy('timestamp', 'desc').limit(3).get();
snapshot.forEach((doc) => {
commit('addPosts', doc.data())
});
console.log('action => ', state.posts.length)
}

Related

NuxtJS dispatch is not loading data

I've been struggling for 5 hours with the following issue.
I have a service file where I have API calls using Axios. In the store, I have an action that uses the service to pull a list of schools, then I commit the data to the mutations. If I console log the data on the mutation object, it works correctly and shows the data. However, when I call dispatch from the component inside the onMounted hook, I get an empty object. Any help is greatly appreciated. (see the code below)
store/schools.js
export const state = () => ({
mySchools: []
});
export const mutations = {
getSchools(state, data) {
state.schools = data;
console.log(state.schools); // works;
}
};
export const actions = {
async getMySchools({ commit }) {
await this.$getSchools().then(response => {
commit("getSchools", response.data);
});
}
};
portal/dashboard.vue
import {onMounted, ref, useStore} from "#nuxtjs/composition-api";
export default {
layout: 'portal',
setup() {
const store = useStore();
const schools = ref([]);
onMounted(async() => {
await store.dispatch('schools/getMySchools'); // is not pulling data
schools.value = store.state.schools.mySchools;
console.log(schools); // empty
});
return {
schools
}
}
};
Thank you
You shouldn't use await with then
try this
async getMySchools({ commit }) {
const response = await this.$getSchools();
commit("getSchools", response.data);
}
I'm assuming that your this.$getSchools() actually works since I'm not sure what that is and it's not part of the code

In Nuxt, how to mutate a Vuex state in one module from the other?

I tried many things mentioned on the portal but nothing seems to work for me so posting here for some work-around.
I have 2 modules within my Nuxtjs application folder store\modules: ModuleA and ModuleB. For some verification in ModuleB I would like to access the state from ModuleA but for some reason, it's failing.
I tried rootScope, import etc but it did not work for me.
My state/modules/ModuleA.js:
export const state = () => ({
eventType: 'MyEvent',
})
export const mutations = {
eventTypePopulator (state, event) {
state.eventType = event
},
}
My state/modules/ModuleB.js:
export const state = () => ({
input: {}
})
export const mutations = {
jsonPreparation ({state, rootState}, payload) {
console.log(rootState.eventType)
// console.log($store.state.ModuleA.eventType)
}
}
I tried to import ModuleA into ModuleB and use it but that also did not work for me. Can someone please help me how can I access the state from one Module in another Module within Nuxtjs/Vuejs
As shown in the API reference, rootState is available in actions and getters.
It did not found any way of using it directly into a mutation.
Meanwhile, this can be achieved by passing it as a param to the mutation like this
ModuleB.js
const mutations = {
NICE_TASTY_MUTATION: (_state, { rootState, payload }) => {
// _state is not used here because it's moduleB's local state
rootState['moduleA'].eventType = payload
},
}
const actions = {
async myCoolAction({ commit, rootState }, { ...}) {
commit('NICE_TASTY_MUTATION', {
rootState,
payload: 'some-stuff'
})
}
}
And this could be called in a .vue file with something like this
methods: {
...mapActions('ModuleB', ['myCoolAction']),
}
...
await this.myCoolAction()
PS: IMO the convention is more to name the file module_b.js.

Async custom hook from within useEffect

When kept in the component body, the following code works fine. Inside useEffect, it checks the asyncstorage and dispatches an action (the function is longer but other checks/dispatches in the function are of the same kind - check asyncstorage and if value exists, dispatch an action)
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
I'm trying to move it to a custom hook but am having problems. The custom hook is:
const useGetUserSettings = () => {
const dispatch = useDispatch();
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
};
export default useGetUserSettings;
Then in the component where I want to call the above, I do:
import useGetUserSettings from './hooks/useGetUserSettings';
...
const getUserSettings = useGetUserSettings();
...
useEffect(() => {
getUserSettings();
}, [getUserSettings])
It returns an error:
getUserSettings is not a function. (In 'getUserSettings()', 'getUserSettings' is undefined
I've been reading rules of hooks and browsing examples on the internet but I can get it working. I've got ESlint set up so it'd show if there were an invalid path to the hook.
Try the following.
useEffect(() => {
if (!getUserSettings) return;
getUserSettings();
}, [getUserSettings]);
The hook doesn't return anything, so it's not surprising that the return value is undefined ;)

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')

How can i pass parameters from method to action in vuejs vuex

I have to files with this code:
Users.vue
methods: {
obtenerUsuarios() {
console.log('Obtener Usuarios')
this.$store
.dispatch('auth/getValidToken')
.then((data) => {
console.log(data). // Console First Message
this.$store
.dispatch('user/fetchUsers', data)
.then((response) => {
this.items = response.data
})
.catch((error) => {
console.log(error)
})
})
.catch((error) => {
console.log('Error: ' + error)
})
},
},
Console Firsts Mesagge show me a json web token in console that is ok.
When i dispatch 'user/fetchUsers in
user.js
export const actions = {
fetchUsers({ jwt }) {
console.log('Action JWT:' + jwt) //Second console.log
return UserService.getUsers(jwt)
},
}
The second messaje show me: Action JWT:undefined in the console
if i change the line two to
fetchUsers(jwt) {
The second messaje show me: Action JwT:[object Object]
I need to pass a json web token from Users.vue method to fetchUsers action y user.js
I will to appreciate any help
Jose Rodriguez
Your action method currently declares the data in the first argument (and no second argument), but actions receive the Vuex context as its first argument. The data payload is in the second argument:
const actions = {
//fetchUsers(data) {} // DON'T DO THIS (1st arg is for context)
fetchUsers(context, data) {}
}