How to watch for state change in Vuex? - javascript

I'm using below code to watch for the change in Vuex state but watch is not working. I get the following error: Uncaught TypeError: Cannot read property 'watch' of undefined
acl.js
import Vue from 'vue'
import { AclInstaller, AclCreate, AclRule } from 'vue-acl'
import router from '#/router'
import axios from '../axios'
import { store } from '../store/store'
Vue.use(AclInstaller)
store.watch(state => state.auth.token, (val) => {
if (val) {
console.log(val)
}
}
)
export default new AclCreate({
initial: 'admin',
notfound: '/pages/not-authorized',
router,
acceptLocalRules: true,
globalRules: new AclRule('admin').generate(),
})
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import auth from "./auth";
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
auth: auth
},
strict: process.env.NODE_ENV !== 'production'
})

Related

Can't use Vuex to display data

I am a new .net+vuejs learner and i'm using
this project template for vuejs
I'm trying to use vuex in my project to display countries data from database but it doesn't work
main.js
import 'bootstrap/dist/css/bootstrap.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
index.js
import { createApp } from "vue";
import App from "./App.vue";
import Vuex from "vuex";
import countries from "./module/countries/countries";
createApp(App).use(Vuex);
export default new Vuex.Store({
modules: {
countries: countries
}
});
store file
import axios from "axios";
const state = {
countries:[]
};
const countries = {
async getCountries({ commit }, payload) {
const result = await axios.get("Pho/GetCountries");
return result.data;
}
};
export default {
namespaced: true,
state,
mutations,
countries,
getters
}
my component
<script>
import axios from 'axios'
import swal from 'sweetalert';
export default {
name: "Home",
data() {
return {
countries: [],
}
},
methods: {
getCountries: async function () {
this.countries = await this.$store.dispatch("countries/getCountries");
}
},
mounted() {
this.getCountries();
}
}
</script>
ERROR: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'dispatch')
i tried to import store to my main.js like this
import 'bootstrap/dist/css/bootstrap.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './index.js'
createApp(App).use(router, store).mount('#app')
but the app doesn't ever lauch
It is not good practice to call APIs from storage files. Use vuex storage for only storage, use mixins(recommended) or component methods for sending API. I will show a simple approach to managing vuex storage!
Storage File: index.js
import Vue from 'vue'
Vuex from 'vuex'
import router from '../router/index'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
countries:[]
},
getters: {},
mutations: {
countriesChanger(state, payload) {
state.countries = payload;
},
},
actions: {},
});
Component File: MyComponent.vue
<template>
<li v-for="country in countries">
{{country}}
</li>
</template>
<script>
export default {
name: "Home",
data() {
return {
countries: [],
}
},
mounted() {
this.getCountries(); // assume this function calls to your API
},
watch: {
"$store.state.countries": function () {
// Listens when countries in storage updated
this.countries = this.$store.state.countries;
}
}
</script>
To change vuex storage item (to update countries) , use mutations after getting data from your API:
this.$store.commit("countriesChanger", countriesFromAPI);
That's it!

Vuex Uncaught Error: [vuex] getters should be function despite getter being a function

Encountering a problem with Vuex 4.0.2 where it throws an uncaught error indicating that a specific getter should be a function, even though it is indeed a function.
Error: Uncaught Error: [vuex] getters should be function but "getters.teamsWithPlayers" is [].
Vue 3.2.31
store/index.js
const getters = {
teamsWithPlayers (state) {
if (state.game.teams === undefined) {
return state.game.teams
}
const teamsWithPlayersList = []
state.game.teams.forEach(function (team) {
const individualScores = []
team.players = state.game.players.filter(player => team.name === player.team)
team.players.forEach(function (player) {
individualScores.push(player.score)
})
team.score = individualScores.reduce(function (a, b) {
return a + b
}, 0)
teamsWithPlayersList.push(team)
})
return teamsWithPlayersList
}
}
export default createStore({
namespaced: true,
state,
mutations,
actions,
getters
})
~
Found the issue was with the creation of the Vue app in main.js. As I was upgrading an existing project from Vue 2 to Vue 3, there was a reference to the old Vuex object instantiation that I did not catch.
The incorrect main.js.
import { createApp } from 'vue'
import Vuex from 'vuex'
import App from './App.vue'
import router from './router'
import store from '#/store'
import '#/assets/styles/reveal_theme.scss'
const vuexStore = new Vuex.Store(store)
const app = createApp(App).use(router).use(vuexStore)
app.mount('#app')
This is fixed by removing the references to the old Vuex.Store.
Corrected main.js:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from '#/store'
const app = createApp(App).use(router).use(store)
app.mount('#app')

How do I enable namespacing on an imported VueX module?

I'm using a helper file to import VueX modules:
const requireModule = require.context('.', false, /\.store\.js$/)
const modules = {}
requireModule.keys().forEach(filename => {
const moduleName = filename
.replace(/(\.\/|\.store\.js)/g, '')
.replace(/^\w/, c => c.toUpperCase())
modules[moduleName] = requireModule(filename).default || requireModule(filename)
})
export default modules
This lives in #/store/modules/index.js and is imported by #/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
actions: {
reset({commit}) {
Object.keys(modules).forEach(moduleName => {
commit(`${moduleName}/RESET`);
})
}
}
})
Imported in to Vue: #/main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Works great for all of my store modules! Each of which are namespaced:
const initialState = () => ({})
const state = initialState()
const mutations = {
RESET(state) {
const newState = initialState();
Object.keys(newState).forEach(key => {
state[key] = newState[key]
});
}
}
const getters = {}
const actions = {}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
Now, I'm trying to import a package as a state module. Which I don't have any experience with. This might seem silly... but I'm not sure how to inject the namespace enablement into the package importation in #/store/modules/Auth.store.js:
import AmazonCognitoVuexModule from 'amazon-cognito-vuex-module';
const cognito = new AmazonCognitoVuexModule({
region: process.env.VUE_APP_COGNITO_REGION,
userPoolId: process.env.VUE_APP_COGNITO_USERPOOL_ID,
clientId: process.env.VUE_APP_COGNITO_CLIENT_ID,
})
export default cognito
So when I try to call the imported store module's actions with $store.dispatch('Auth/...') they're not found... because they're not namespaced. I want to namespace this module "Auth". I bet I'm overlooking something really simple. Any help appreciated.
My first try would be (based on the docs of this package on NPM):
// source: https://www.npmjs.com/package/amazon-cognito-vuex-module
const store = new Vuex.Store({
modules: {
cognito: new AmazonCognitoVuexModule({
region: '<region>',
userPoolId: '<user pool id>',
clientId: '<client id>'
})
}
});
The code above shows that you can import it as a module (they named it cognito, you want auth - that should make no difference; I use the naming in the official docs). So you need to update your code, like this:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
import cognito from './path/to/cognito'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
...modules,
cognito,
},
actions: {
reset({commit}) {
Object.keys(modules).forEach(moduleName => {
commit(`${moduleName}/RESET`);
})
}
}
})
I'm not sure this works, but this would be my first try :)

Couldn't access state in vuex 4

I'm using vuex v4 with composition api. The problem is I couldn't access store states in components but I can see them in vue-devtools. When I wanna access store state it gave me undefined.
store/index.ts:
import { InjectionKey } from "vue";
import {
createStore,
createLogger,
Store,
useStore as vuexUseStore,
} from "vuex";
import { ConfigStateType } from "./config/state";
import config from "./config";
export enum ModuleTypes {
CONFIG = "CONFIG",
}
export interface StateInterface {
config: ConfigStateType;
}
export const key: InjectionKey<Store<StateInterface>> = Symbol();
export const store = createStore<StateInterface>({
modules: {
[ModuleTypes.CONFIG]: config,
},
strict: debug,
});
export function useStore() {
return vuexUseStore(key);
}
vue js main.ts:
import { store, key } from "./store";
createApp(App).use(store, key).use(router).mount("#app");
In my component:
import { useStore } from "#/store";
setup() {
const store = useStore();
const recaptchaSiteKey = computed(
() => store.state.config.googlerecaptcha_site_key
);
return {
recaptchaSiteKey
}
}
always recaptchaSiteKey returns undefined, but in vue-devtools states are filled.
vuex devtool:

Why is setting Vue.http for vue-resource ignored?

I'm using Vue.js 2.3.3, Vue Resource 1.3.3, Vue Router 2.5.3, and I'm trying to set up Vue-Auth. I keep getting a console error, however, that says auth.js?b7de:487 Error (#websanova/vue-auth): vue-resource.1.x.js : Vue.http must be set.. I'm setting Vue.http in main.js, but vue-resource is not picking it up for some reason.
main.js:
import Vue from 'vue'
import Actions from 'actions'
import App from './App'
Vue.use(Actions, {
locales: ['en', 'zh', 'fr']
})
Vue.http.options.root = 'https://api.example.com'
new Vue({
render: h => h(App),
watch: {
lang: function (val) {
Vue.config.lang = val
}
}
}).$mount('#app')
actions/index.js
import VueResource from 'vue-resource'
import Router from 'actions/router'
import I18n from 'actions/i18n'
export default {
install (Vue, options) {
Vue.use(Router)
Vue.use(I18n, options.locales)
Vue.use(require('#websanova/vue-auth'), {
router: require('#websanova/vue-auth/drivers/router/vue-router.2.x'),
auth: require('#websanova/vue-auth/drivers/auth/bearer'),
http: require('#websanova/vue-auth/drivers/http/vue-resource.1.x')
})
}
}
And if I add Vue.use(VueResource) to actions/index.js right below Vue.use(Router), I get a new error: Error (#websanova/vue-auth): vue-router.2.x.js : Vue.router must be set.
On the other hand, if I move Vue.http.options.root = 'https://api.example.com' to right below the import statements, I get yet another error: Uncaught TypeError: Cannot read property 'options' of undefined
You need to import 'vue-resource' in to your main.js file to get ride of this errors:
import Vue from 'vue'
import VueResource from 'vue-resource';
import Actions from 'actions'
import App from './App'
Vue.use(Actions, {
locales: ['en', 'zh', 'fr']
})
Vue.use(VueResource)
Vue.http.options.root = 'https://api.example.com'
new Vue({
render: h => h(App),
watch: {
lang: function (val) {
Vue.config.lang = val
}
}
}).$mount('#app')
Using axios and not vue-resource this is a working setup for me:
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueRouter)
Vue.use(VueAxios, axios)
Vue.router = new VueRouter({
// Your routes.
})
Vue.use(require('#websanova/vue-auth'), {
auth: require('#websanova/vue-auth/drivers/auth/bearer.js'),
http: require('#websanova/vue-auth/drivers/http/axios.1.x.js'),
router: require('#websanova/vue-auth/drivers/router/vue-router.2.x.js'),
})
App.router = Vue.router
new Vue(App).$mount('#app')
For more guidance you can refer to this great tutorial: https://codeburst.io/api-authentication-in-laravel-vue-spa-using-jwt-auth-d8251b3632e0

Categories