I have been trying to use Pinia for state management in my vue application. I've been running into the issue where pinia is being used before its initialised in the main.js folder.
This is the routes file.
import { createRouter, createWebHistory } from 'vue-router'
import {useUserStore} from '../stores/user'
const store = useUserStore()
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'login',
component: () => import('../views/LoginView.vue')
},
{
path: '/Dash',
name: 'dash',
component: () => import('../views/authed/DashView.vue'),
// beforeEnter: (to, from) => {
// }
},
{
path: '/Register',
name: 'register',
component: () => import('../views/RegisterView.vue')
}
]
})
export default router
This is the place where is store the data. user.js
import { ref, computed, watch } from 'vue'
import { defineStore } from 'pinia'
import axios from 'axios'
export const useUserStore = defineStore('user', () => {
const user = ref ({
name: "",
token: "",
auth: false,
})
// if (localStorage.getItem('user')) {
// user.value = JSON.parse(localStorage.getItem('user'))
// }
// watch(
// user,
// (userVal) => {
// localStorage.setItem('User', JSON.stringify(userVal))
// },
// { deep: true}
// )
const setAuth = (newToken, successAuth, authedName) => {
user.value.token = newToken
user.value.auth = successAuth
user.value.name = authedName
}
const checkAuth = () => {
try {
const response = axios.post('api/get-user', {}, {
headers: {
Authorization: `Bearer ${user.value.token}`
}
})
.then(({ data: userData }) => {
console.log('USER IS AUTHED')
})
} catch (err) {
console.log(err)
}
}
return {
user,
setAuth,
checkAuth,
}
},
{
persist: true
})
This is the main.js file.
import { createApp, watch } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
import router from './router'
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"
import axios from 'axios'
import './assets/main.css'
const pinia = createPinia()
// if (localStorage.getItem('user')) {
// pinia.user.value = JSON.parse(localstorage.getItem('user'))
// }
// watch(
// pinia.state,
// (state) => {
// localStorage.setItem('state', JSON.stringify(state))
// },
// { deep: true }
// )
const app = createApp(App)
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
app.use(router, axios)
app.mount('#app')
This is my error...
Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
at useStore (pinia.js?v=3c6f7703:1256:13)
at index.js?t=1658100067018:5:9
The error happens when I add to the router file, I need to use it here so I can authenticate users to use certain routes.
import {useUserStore} from '../stores/user'
store = useUserStore()
This is the edit I've made to the main.js file from the first answer, however moving the place I import the file hasn't made a difference still getting the same error.
import { createApp, watch } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"
import axios from 'axios'
import './assets/main.css'
const pinia = createPinia()
const app = createApp(App)
pinia.use(piniaPluginPersistedstate)
app.use(pinia)
import router from './router'
app.use(router, axios)
app.mount('#app')
in your main.js file when you import router from './router' this line will be excute const store = useUserStore() at this time Pinia is not define yet.
Related
i can't get data from Vuex,
in one component this.$store works, but in others components don't work.
Component which work perfectly is SignIn, but in HomeView and List this.$store get me error like : Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '$store')
My store.js:
import { createStore } from "vuex";
const store = createStore({
state: {
logged: false,
token: "",
},
mutations: {
signIn(state) {
state.logged = true;
},
logOut(state) {
state.logged = false;
},
updateToken(state, token) {
state.token = token;
},
},
getters: {
token: (state) => state.token,
}
});
export default store;
main.js:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap";
import store from "./store/store";
import "./assets/sbx.css";
const app = createApp(App);
app.use(store);
app.use(router);
app.mount("#app");
SignIn function with this.$store
import { defineComponent, ref } from "vue";
import { signIn } from "../../services/Auth/signIn";
const userName = ref('');
const password = ref('');
async function signInUser()
{
const response = await signIn(userName.value, password.value);
if (response.status === 200) {
this.$store.commit("signIn");
this.$store.commit("updateToken", response.data.token);
}
}
export default defineComponent({
setup() {
return {
signInUser,
userName,
password,
};
},
});
And HomeView where this.$store dont work
import { defineComponent } from "vue";
const test = this.$store.getters.token;
export default defineComponent({
setup() {
return {
};
},
});
This is my code. Can some please help me figure out the error.I am using jest to test out my frontend which I have built using Vue.The line const localVue = createLocalVue(); is giving out the error TypeError: (0 , _testUtils.createLocalVue) is not a function
import { createLocalVue,shallowMount } from '#vue/test-utils'
import Vuex from 'vuex'
import getters from '../../src/store/module/auth/getters.js'
import TheHeader from '#/components/layout/TheHeader.vue'
// const store = new Vuex.Store({
// state: {
// user:null,
// token:'',
// expiresIn:null,
// isUserLoggedIn:false,
// isAdminLoggedIn:false,
// }
// })
describe('TheHeader', () => {
const localVue = createLocalVue();
localVue.use(Vuex);
let store
let state
it('Checks whether the login is correctly displayed', () => {
const cmp = shallowMount(TheHeader, { store,localVue})
expect(cmp.name()).toMatch('TheHeader')
expect(cmp.vm.isLoggedIn()).toBe(false)
})
})
createLocalVue was removed in version 2 of #vue/test-utils, which explains why it's undefined in your example.
To install a Vue plugin (such as Vuex), use the global.plugins mounting option
To mock instance APIs (such as this.$store), use the global.mocks mounting option
import Vuex from 'vuex'
import { shallowMount } from '#vue/test-utils'
import TheHeader from '#/components/TheHeader.vue'
const store = /* Vuex store */
const cmp = shallowMount(TheHeader, {
global: {
plugins: [Vuex],
// OR:
mocks: {
$store: store,
}
}
})
import { mount } from '#vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '#/views/Home'
creating a fake store
const store = createStore({
state() {
return {
count: 0,
user: {},
}
},
mutations: {
increment(state) {
state.count += 1
},
},
})
Creating the Component
const app = createApp(App)
app.use(store)
let wrapper
beforeEach(() => {
wrapper = mount(App, {
global: {
plugins: [store],
},
computed: { showAlert: () => false },
})
})
now you can do the test
test('Home', async () => {
expect(wrapper.vm.showAlert).toBe(false)
})
I am using Vee-Validate plugin for form validation in my VueJS Application. So, my app has more than 1 language, for that, I am using I18n. All the plugins I am using are in separate files under plugins folder and then I am getting all files and registering all plugins in main.js, so in my Vee-Validate.js I have written:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import VeeValidate from 'vee-validate';
import enMessages from "./../locales/validation/en";
import urMessages from "./../locales/validation/ur";
Vue.use(VueI18n);
const i18n = new VueI18n();
i18n.locale = "en";
Vue.use(VeeValidate, {
errorBagName: 'vErrors',
i18nRootKey: 'validations',
i18n,
dictionary: {
en: {
messages: enMessages
},
ur: {
messages: urMessages
}
}
});
But on clicking the change locale button don't change this file locale,
My change locale function:
changeLocale () {
this.$i18n.locale == 'en' ? this.$i18n.locale = 'ur' : this.$i18n.locale = 'en'
this.$vuetify.rtl = this.$i18n.locale == 'ur' ? true : false;
}
Well, I'm not saying your configuration is wrong. I'm going just share mine that is working just fine.
1- vue.config.js
module.exports = {
transpileDependencies: [
'vuetify',
],
pluginOptions: {
i18n: {
locale: 'en',
fallbackLocale: 'en',
localeDir: 'locales',
enableInSFC: true,
},
},
};
2- i18n.ts
import Vue from 'vue';
import VueI18n, { LocaleMessages } from 'vue-i18n';
Vue.use(VueI18n);
function loadLocaleMessages(): LocaleMessages {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i);
const messages: LocaleMessages = {};
locales.keys().forEach((key) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
messages[locale] = locales(key);
}
});
return messages;
}
export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages(),
});
3- main.ts
import Vue from 'vue';
import './registerServiceWorker';
import { sync } from 'vuex-router-sync';
import VueLodash from 'vue-lodash';
import Storage from 'vue-ls';
import vuetify from './plugins/vuetify';
import './utils/vee-validate';
// Components
import './components';
// Application imports
import App from './App.vue';
import router from '#/router';
import store from '#/store';
import i18n from './i18n';
// Sync store with router
sync(store, router);
const options = {
name: 'ls', // name variable Vue.[ls] or this.[$ls],
storage: 'local', // storage name session, local, memory
};
Vue.use(Storage, options);
Vue.config.productionTip = false;
Vue.use(VueLodash, { name: 'lodash' });
new Vue({
router,
store,
vuetify,
i18n,
render: h => h(App),
}).$mount('#app');
4- src/plugins/vuetify.ts
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
// import VueI18n from 'vue-i18n';
// import i18n from '#/i18n';
import en from '#/locales/en.json';
import jp from '#/locales/jp.json';
Vue.use(Vuetify);
export default new Vuetify({
lang: {
locales: { en, jp },
current: 'jp',
},
});
5- src/utils/vee-validate.js
/* eslint-disable no-underscore-dangle */
import { extend } from 'vee-validate';
import { required, email, confirmed } from 'vee-validate/dist/rules';
import i18n from '#/i18n';
extend('required', {
...required,
message: (_, values) => i18n.t('GENERAL_VALIDATION_MESSAGES_REQUIRED', values),
});
extend('email', {
...email,
message: (_, values) => i18n.t('LOGIN_FORM_EMAIL_VALID_MESSAGE', values),
});
extend('confirmed', {
...confirmed,
message: (_, values) => i18n.t('CHANGE_PASSWORD_FORM_CONFIRMATION_VALID_MESSAGE', values),
});
6- I use vuex so from my language store
import Vue from 'vue';
import { localize } from 'vee-validate';
import Vuetify from 'vuetify/lib';
import i18n from '#/i18n';
import en from '#/locales/en.json';
import jp from '#/locales/jp.json';
...
const mutations = {
SET_LANG(state, data) {
state.lang = data;
i18n.locale = data;
localize(data, jp);
},
SET_LANG_ERROR() {
window.$messageGlobal('Error switching languages');
},
};
Hope it helps
I have a vuex store like this:
// store.js
import Vuex from 'vuex'
import router from '../router' // this is a vuejs router
export const actions = {
async load({ dispatch, commit }) {
if (router.currentRoute.name === 'one-route') {
dispatch('oneModule/oneAction', null, { root: true })
}
}
}
export default new Vuex.Store({
state,
actions,
...
})
I would like to test it with Jest.
// store.test.js
import { createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import { actions } from './store'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)
describe('store tests', () => {
let store, router, oneAction
beforeEach(() => {
oneAction = jest.fn()
const modules = {
oneModule: {
namespaced: true,
actions: { oneAction }
}
}
store = new Vuex.Store({ actions, modules })
router = new VueRouter({ routes: [{ name: 'one-route' }] })
}
test('call module action if one-route is selected', async () => {
router.push({ name: 'one-route' })
await store.dispatch('load')
expect(oneAction).toHaveBeenCalled()
})
}
This makes the following error:
Expected mock function to have been called, but it was not called.
What is the correct way to mock the router to make this test pass?
Thank you
I have created login page. router intercepting the request and validates the user is authenticated or not . store is maintaining the user is logged in or not.
while debugging i am getting in auth.js
"store is not defined"
I also tried relative path instead of # in imports.
router code snippet
import auth from '#/services/auth';
...
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!auth.loggedIn()) {
next({
path: '/login',
});
} else {
next();
}
} else {
next();
}
});
auth.js is like service which will interact with store and maintain state .
import Vue from 'vue';
import axios from 'axios';
import VueAxios from 'vue-axios';
import store from '#/store/UserStore';
Vue.use(VueAxios, axios);
export default {
login(credentials) {
return new Promise((resolve, reject) => {
Vue.axios.post('/api/authenticate', credentials).then((response) => {
localStorage.setItem('token', response.body.token);
store.commit('LOGIN_USER');
resolve();
}).catch((error) => {
store.commit('LOGOUT_USER');
reject(error);
});
});
},
isUserLoggedIn() {
return store.isUserLoggedIn();
},
};
here is my store to
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
state: {
isLogged: !localStorage.getItem('token'),
user: {},
},
actions: {
},
mutations: {
/* eslint-disable no-param-reassign */
LOGIN_USER(state) {
state.isLogged = true;
},
/* eslint-disable no-param-reassign */
LOGOUT_USER(state) {
state.isLogged = false;
},
},
getters: {
isUserLoggedIn: state => state.isLogged,
},
modules: {
},
});
change export type in UserStore like this:
line
export default new Vuex.Store({
replace with
export const store = new Vuex.Store({