I created a store action that fetch api. When I'm trying to dispatch it from component in created lifecycle hook. I'm getting Cannot read property 'dispatch' of undefined error. I know there are several similar questions but none of them solved this issue.
I tried to dispatch it also in normal method and still get this error.
store.js
import Vue from "vue";
import Vuex from "Vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
categories: []
},
mutations: {
SET_CATEGORIES(state, categories) {
state.categories = categories;
}
},
actions: {
getCategories({ commit }) {
return fetch("https://api.chucknorris.io/jokes/categories")
.then(response => {
return response.json();
})
.then(jsonObj => {
commit("SET_CATEGORIES", jsonObj);
})
.catch(error => {
console.log(error);
});
}
}
});
And this is the component which I try to dispatch in -
<script>
export default {
data() {
return {
joke: "",
categories: [],
selectedCat: ""
};
},
computed: {
disabled() {
if (this.joke) {
return false;
} else {
return true;
}
}
},
methods: {
addToFavs: function() {
this.$emit("pushJoke", this.joke);
this.fetchJoke();
}
},
created() {
this.$store.dispatch('getCategories');
}
};
</script>
What am I doing wrong?
Start by adding
import { mapActions } from 'vuex'
in your script
then add to methods
methods: {
...mapActions([
'getCategories'
])
.
.
}
and alter your created method to
created() {
this.getCategories()
}
Furthermore, you might want to create a vuex action to replace that this.$emit("pushJoke", this.joke); line that you have, and map that in a similar way that you have mapped getCategories
That's because you missed to add your vuex store option to the root instance of Vue. To solve that import your store and attach it to your vue instance.
import { store } from '.store'
const app = new Vue({
store
})
Related
I am trying to access subjects store state using this.$store.state.subjects inside my home component however it comes up as an empty array. Using console.log the only place I am able to see the state.subjects populated is if its in the mutation function.
Anywhere else the console.log is empty. It seems to me that the state is not persisting from the mutations, but I'm not sure why.
I have tried quite a few stackoverflow answers however, non of them fix the issues or I have no clue what I am reading in the post. I have also left of code from my code blocks to make this post more readable, such as imports or templates.
Store index.js
export default createStore({
state: {
subjects: [],
},
actions: {
getSubjects({ commit }) {
// Manages subjects, allow for display in column or Calendar view
axiosMain({
method: "get",
url: "/study/",
withCredentials: true,
})
.then((response) => {
commit("setSubjects", response.data);
})
},
},
mutations: {
setSubjects(state, subjectsToSet) {
state.subjects = subjectsToSet;
console.log(state.subjects) # Is a populated array
}
}
});
Main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import VueGtag from "vue-gtag-next";
import store from "./store";
import "./assets/main.css";
createApp(App)
.use(router)
.use(store)
.use(VueGtag, {
property: {
id: "G-E4DPXQ96HB",
},
})
.mount("#app");
Home.vue
<template>
</template>
<script>
export default {
name: "Home",
data() {
return {
subjects: [],
};
},
mounted() {
this.callStoreSubjectAction();
this.setSubjectsToStoreSubject();
},
methods: {
callStoreSubjectAction() {
this.$store.dispatch("getSubjects");
},
setSubjectsToStoreSubject() {
this.subjects = this.$store.state.subjects;
console.log(this.$store.state.subjects); # Is an empty array
},
},
};
</script>
In the component, you're copying the value of this.$store.state.subjects before the axios call has completed. Wait for the promise to resolve first. To do that, you'll need to first return the promise from the action:
getSubjects({ commit }) {
return axiosMain({ // returning the promise
...
}
}
Waiting for the promise:
mounted() {
this.$store.dispatch("getSubjects").then(r => {
this.subjects = this.$store.state.subjects;
console.log(this.$store.state.subjects);
});
},
Better than this would be to remove subjects from your component data and use a computed instead to sync with the Vuex state:
import { mapState } from 'vuex';
computed: {
...mapState(['subjects']) // creates a computed `this.subjects`
}
Then you would only have to dispatch the action and the component will take care of the rest:
mounted() {
this.$store.dispatch("getSubjects");
}
My goal is to load a firestore collection into the Vuex state and use it for multiple pages. I was trying to follow :
How to get collection from firestore and set to vuex state when the app is rendered?
I followed this post and either I am missing something or possibly the code is outdated? I am very new to Vuex so I could be doing something wrong but I am not sure.
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
const db = require('../components/fbInit')
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
categories: []
},
actions: {
fetchCategories({ commit }) {
db.collection('one').get().then(querySnapshot => {
if (querySnapshot.empty) {
// eslint-disable-next-line no-console
console.log('cannot find')
//this.$router.push('/HelloWorld')
} else {
this.loading = false;
var categories = [];
querySnapshot.forEach(doc => {
categories.push(doc.data());
});
commit("setCategories", categories);
}
});
}
},
mutations: {
setCategories(state, val) {
state.categories = val;
}
}
});
store.dispatch("fetchCategories");
export default store;
One.vue
<template>
<div class="flex-row justify-center ma-12">
<ul>
<li v-for="category in categories" :key="category.name">{{category.name}}</li>
</ul>
</div>
</template>
<script>
import { mapActions } from "vuex";
import { mapGetters } from "vuex";
// eslint-disable-next-line no-unused-vars
export default {
computed: {
categories() {
return this.$store.state.categories;
},
...mapGetters([])
},
methods: {
...mapActions(["fetchCatagories"])
}
};
</script>
I tested to see if the firestore is connected and it displayed its contents. But I am getting this error: Uncaught TypeError: db.collection is not a function.
I have never been able to load my firestore collection into the Vuex store state and load it. Again I am new to using vuex so any help will be greatly appreciated
TLDR; Goal: Load firestore collection('One'), Store it in Vuex state, Use Vuex store to load the same data on multiple pages without having to call the data on every page.
store/index.js
const store = new Vuex.Store({
....
getters: {
categories (state) {
return state.categories;
}
},
....
});
One.vue
export default {
....
computed: {
...mapGetters(['categories'])
},
....
}
const db = require('../components/fbInit') might be your problem here. In a firestore/vue project I own, my db is declared as:
import firebase from '../firebase';
const db = firebase.firestore();
I'm able to console data, as well as able to see data in vuex dev tool
but not able to display them in table. Please if someone could check
my code and tell me what is wrong with it. Thanks folks. I tried differents methods like async/await, promise, getters... but I was not able to to get the data, probably I was not calling it properly.
ROLE.VUE
<emplate>
<di>
<p v-for="(role, index) in roles :key="index">
</div>
</template>
<script>
import { mapState } from 'vuex'
export default ({
name: 'Role',
metaInfo: {
title: 'Role'
},
created () {
this.$store.dispatch('loadRoles').then((response) => { console.log(response) })
},
computed: {
...mapState([
'roles'
])
}
})
</script>
role.js
import Axios from 'axios'
export default {
// namespaced: true,
state: {
roles: [],
},
getters: {
roles (state) {
return state.roles
}
},
mutations: {
SET_ROLES (state, roles) {
state.roles = roles
}
},
actions: {
loadRoles ({ commit }) {
Axios.get('/settings/roles')
.then((response) => {
console.log(response.data)
// let roles = response.data
commit('SET_ROLES', response.data.roles)
})
}
}
}
index.js
import role from './role'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
},
mutations: {
//
},
modules: {
role
},
actions: {
//
}
})
Main.js
import { store } from './store/store'
new Vue({
router,
store,
ValidationProvider,
render: h => h(App)
})
When loading from a module with mapState, you need to specify the name of the module you are loading from. Here is the proper syntax for your case:
...mapState('role', ['roles'])
The first argument is the module name, and the second is an array of state properties from that module.
sorry about my english, How can i in other js files use vuex.store in nuxt project
in store
export const state = () => ({
token: 'test',
name: '',
avatar: ''
}),
export const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
}
},
export const getters = {
token: state => {
return state.token
}
}
in test.js
export function() => {
//how can i updata vuex token?
}
export function() => {
//how can i getter vuex token?
}
export default ({ app, store, route, redirect }) => {
some code
}
it can't work
Wanted to know if it's good practice to do that and what would be the best way to do that?
A basic implementation would look like this
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
computed: {
// you only need State OR Getter here not both!!! You don't need a
// getter for just returning a simple state
...mapState('yourStoreName', ['token'])
...mapGetters('yourStoreName', ['token']),
},
methods: {
methodThatNeedsToChangeState (){
this.setToken('newToken')
},
...mapActions('yourStoreName', ['setToken']),
}
}
In your store you need actions though, you don't call mutations directly! Because Mutations can't be asynchronous.
export const actions = {
setToken: (context, token) => {
context.commit(SET_TOKEN, token)
}
},
I would highly recommend you to study the Vuex documentation in more detail.
https://vuex.vuejs.org/guide/
I'm getting the following error.
[Vue warn]: Property or method "updateData" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
As far I can tell by the code, the method is there, so I'm stuck on something that I miss due to my ignorance of Vuex. I've googled the matter and got quite a few answers but none of them made me any wiser what to do. It seems to be something with scope, I'm sensing.
I also get the error below but I suspect that it's the same root cause for both so solving the one will resolve the other.
[Vue warn]: Invalid handler for event "click": got undefined
(found in component at ...)
The markup is as follow. I've checked that the path goes to the right location. At the moment I'm not sure at all how to even start to troubleshoot it. Any hints would be appreciated.
<template>
<div id="nav-bar">
<ul>
<li #click="updateData">Update</li>
<li #click="resetData">Reset</li>
</ul>
</div>
</template>
<script>
import { updateData, resetData } from "../vuex_app/actions";
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData }
}
}
</script>
Edit
After input I improved the export to include methods property like so. (Still the same error remaining, though.)
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData },
methods:{
updateData: () => this.$store.dispatch("updateData"),
resetData: () => this.$store.dispatch("resetData")
}
}
}
Do I have to do something extra in the store? It looks like this.
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = { dataRows: [], activeDataRow: {} };
const mutations = {
UPDATE_DATA(state, data) { state.dataRows = data; state.activeDataRow = {}; },
RESET_DATA(state) { state.dataRows = []; state.activeDataRow = {}; }
};
export default new Vuex.Store({ state, mutations });
You have to add the imported functions in the methods of Vue component, like following. You can take help of mapActions as explained in the documentation. This is needed to map this.updateDate to this.$store.dispatch('updateDate').
<script>
import { updateData, resetData } from "../vuex_app/actions";
import { mapActions } from 'vuex'
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData }
},
methods: {
...mapActions(['updateData', 'resetData'])
}
}
</script>
Edited
In case you dont want to use mapActions, you can use this.$store.dispatch as you are using in your example, however you need to have methods at vue compoenent level (documentation) and not insise vuex, as following:
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData }
},
methods:{
updateData: () => this.$store.dispatch("updateData"),
resetData: () => this.$store.dispatch("resetData")
}
}