Vue3 - Persisting state with Page refresh - vue.js

I have just started using Vue3, and am trying to use vuex to manage state. when using Vue2 I would call the store each time the app loads like this:
// mains.js
import VueRouter from "vue-router";
import Vuex from "vuex";
import router from "./routes";
window.Vue = require('vue').default;
Vue.use(VueRouter);
Vue.use(VueMoment);
Vue.use(Vuex);
const store = new Vuex.Store(storeDefinition);
const app = new Vue({
el: '#app',
router,
store,
components: {
"index": Index
},
async created() {
this.$store.dispatch("loadStoredState");
this.$store.dispatch("loadUser");
},
});
This is my vuex store that defines state, mutations and actions for vuex:
// store.js
import { isLoggedIn, logOut } from "./shared/utils/auth";
export default {
state: {
isLoggedIn: false,
user: {}
},
mutations: {
setUser(state, payload) {
state.user = payload;
},
setLoggedIn(state, payload) {
state.isLoggedIn = payload;
}
},
actions: {
loadStoredState(context) {
context.commit("setLoggedIn", isLoggedIn());
},
async loadUser({ commit, dispatch }) {
if (isLoggedIn()) {
try {
const user = (await axios.get("/user")).data;
commit("setUser", user);
commit("setLoggedIn", true);
} catch (error) {
dispatch("logout");
}
}
},
logout({ commit }) {
commit("setUser", {});
commit("setLoggedIn", false);
logOut();
}
},
getters: {}
}
This file manages a cookie for local storage that stores a boolean value for isLoggedIn:
// auth.js
export function isLoggedIn() {
return localStorage.getItem("isLoggedIn") == 'true';
}
export function logIn() {
localStorage.setItem("isLoggedIn", true);
}
export function logOut() {
localStorage.setItem("isLoggedIn", false);
}
But in Vue3 I am creating the main.js file like this:
// mains.js
const { createApp } = require('vue')
import Index from './Index.vue'
import createRouter from './router'
import { createStore } from 'vuex'
import storeDefinition from "./store";
const store = createStore(storeDefinition)
createApp(Index)
.use(createRouter())
.use(store)
.mount('#app')
How can I add the two calls to manage the store to the createApp function?

You can add the created hook to the root component by using the extends option with the component definition:
// main.js
import { createApp } from 'vue'
import Index from './Index.vue'
import createRouter from './router'
import { createStore } from 'vuex'
import storeDefinition from './store'
const store = createStore(storeDefinition)
createApp({
extends: Index,
created() {
this.$store.dispatch('loadStoredState')
this.$store.dispatch('loadUser')
},
})
.use(createRouter())
.use(store)
.mount('#app')
demo

Related

Vue3 Pinia Store cannot access 'store" before initializsation

I have a UserStore which contains some information about the current user. This store also is responsible for loggin in and out.
In order to make the getters available I map the getters to my computed attribute within my Vue component.
Unfortunately I get an error saying that it cannot access useUserStore before initilization.
This is my component:
<template>
//...
</template>
<script>
import {mapState} from "pinia"
import {useUserStore} from "../../stores/UserStore.js";
import LoginForm from "../../components/forms/LoginForm.vue";
export default {
name: "Login",
components: {LoginForm},
computed: {
...mapState(useUserStore, ["user", "isAuthenticated"]) //commenting this out makes it work
}
}
</script>
This is my store:
import { defineStore } from 'pinia'
import {gameApi} from "../plugins/gameApi.js"
import {router} from "../router.js";
export const useUserStore = defineStore("UserStore", {
persist: true,
state: () => ({
authenticated: false,
_user: null
}),
getters: {
user: (state) => state._user,
isAuthenticated: (state) => state.authenticated
},
actions: {
async checkLoginState() {
// ...
},
async loginUser(fields) {
// ...
},
async logutUser() {
// ...
}
}
})
And my main.js
import {createApp} from 'vue'
import App from './App.vue'
import gameApi from './plugins/gameApi'
import {router} from './router.js'
import store from "./stores/index.js";
createApp(App)
.use(store)
.use(router)
.use(gameApi)
.mount('#app')
And finally my store configuration:
import {createPinia} from "pinia"
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import {useUserStore} from "./UserStore.js";
const piniaStore = createPinia()
piniaStore.use(piniaPluginPersistedstate)
export default {
install: (app, options) => {
app.use(piniaStore)
const userStore = useUserStore()
const gameStore = useGameStore()
}
}
I wasn't able to initialize the store using the "old" way comparable with Vuex. Instead I had to make use of the setup() function Pinia Docu:
<script>
import {useUserStore} from "../../stores/UserStore.js";
export default {
name: "RegisterForm",
setup() {
// initialize the store
const userStore = useUserStore()
return {userStore}
},
data() {
return {
// ...
}
},
methods: {
checkLoginState() {
this.userStore.checkLoginState()
}
}
}
</script>

Why Vue doesn't see vuex?

I work with microfrontend single-spa (https://single-spa.js.org/)
I create a store
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
}
})
in src/main.js i import it
import Vue from 'vue';
import singleSpaVue from 'single-spa-vue';
import 'devextreme/dist/css/dx.light.css';
import App from './App.vue';
import store from './store';
console.log("πŸš€ ~ file: main.js ~ line 6 ~ store", store)
Vue.config.productionTip = false;
Vue.prototype.$bus = new Vue();
const vueLifecycles = singleSpaVue({
store,
Vue,
appOptions: {
render(h) {
return h(App, {
store,
props: {
},
});
},
},
});
export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;
in console.log store displayed, but in the app cnsole.log(this) does not contain store
I don't understand where I am connecting wrong?
According to the docs, store should be a subproperty of appOptions:
const vueLifecycles = singleSpaVue({
// store, ❌
Vue,
appOptions: {
store, βœ…
render(h) {
return h(App, {
// store, ❌
props: {},
})
},
},
})

Using vuex PersistedState with vuex modules

I want to use PersistedState https://github.com/robinvdvleuten/vuex-persistedstate with vuex but i can't get to setup correctly.
I have this module inside the store directory
export const auth = {
namespaced: true,
state: {
},
getters: {
countLinks: state => {
return state.links.length
}
},
mutations: {
SET_LINKS: (state, links) => {
state.links = links;
},
//Synchronous
ADD_LINK: (state, link) => {
state.links.push(link)
},
REMOVE_LINK: (state, link) => {
state.links.splice(link, 1)
},
REMOVE_ALL: (state) => {
state.links = []
}
},
actions: {
//Asynchronous
removeLink: (context, link) => {
context.commit("REMOVE_LINK", link)
},
removeAll ({commit}) {
return new Promise((resolve) => {
setTimeout(() => {
commit('REMOVE_ALL')
resolve()
}, 1500)
})
}
}
}
I have named this Γ uth.js
This is my index.js file also inside store directory
import { createStore } from 'vuex'
import createPersistedState from "vuex-persistedstate"
import { auth } from './auth'
const dataState = createPersistedState({
paths: ['data']
})
const store = createStore({
modules: {
auth
},
plugins: [dataState]
})
I have a total of 7 modules i would like to load and use in various places in my application. To kick things off i just want to load auth module and use it in my home.vue page
This is the script section of my home.vue
import Footer from '#/components/Footer.vue'
import Header from '#/components/Header.vue'
import { mapGetters} from 'vuex'
import store from '../store';
export default {
name: 'Home',
components: {
Footer,Header
},
mounted () {
var links = ['http://google.com','http://coursetro.com','http://youtube.com'];
store.commit('SET_LINKS', links);
},
computed: {
...mapGetters([
'countLinks'
]),
}
}
This is my main.js file
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import i18n from './i18n'
import FlagIcon from 'vue-flag-icon'
import {store} from './store';
createApp(App).use(i18n).use(FlagIcon).use(store).use(router).mount('#app')
When i npm run serve, i get the error
10:7 error 'store' is assigned a value but never used
no-unused-vars
How should i correct this to be able to use auth.js module anywhere in my application?

IONIC + Vue.js not working getters in VUEX

Hello developers I'm working with Ionic and Vue+Vuex in this app, but eventually, I can't access my state from my getters for some reason.
As usually the management state is settled for that purpose with actions getters, mutations, and state.
import { createStore } from "vuex";
const urlLogin = "http://localhost:3006/auth";
const urlUser = "http://localhost:3006/user";
const store = createStore({
state() {
return {
userRegisteredState: false,
allUsersState: [],
};
},
mutations: {
commit_get_all_users(state, payload) {
console.log(payload);
return (state.allUsersState = payload);
},
},
getters: {
getterGetAllUsers(state) {
console.log(state);//checking all state (See image )
console.log(state.allUsersState)//checking only the item I want to consume from state (See image )
return state.allUsersState;
},
},
actions: {
//=================================================================
//============================================================
async getAllUsers({ commit }) {
fetch(`${urlUser}/get/all/users`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "http://localhost:8100",
},
})
.then((result) => {
return result.json();
})
.then((result) => {
console.log(result);
commit("commit_get_all_users", result);
})
.catch((error) => {
console.log(error);
error;
});
},
},
});
export default store;
then on my component , in this case that one referring to get all users I set this logic:
<template>
...some tags...
</template>
<script>
import { mapActions, mapGetters } from "vuex";
export default {
name: "AllUsersComponent",
data() {
return {
allUsers: [],
};
},
methods: {
...mapActions(["getAllUsers"]),
getAllUsersFront() {
this.$store.dispatch("getAllUsers");
},
...some methods....
},
computed: {
...mapGetters(["getterGetAllUsers"]),
getterGetAllUsersFunction() {
console.log(this.$store.getters.getterGetAllUsers);
return this.$store.getters.getterGetAllUsers;
},
},
async mounted() {
this.getAllUsersFront();
},
created() {
this.getAllUsersFront();
this.getterGetAllUsersFunction;
console.log(this.getterGetAllUsersFunction);
},
watch: {},
};
</script>
<style>
</style>
On my main.js file store is imported according to IONC in this way
import { createApp } from "vue";
import './gapi.js'
import App from "./App.vue";
import router from "./router";
import store from "./store/index"; //importando vuex
import loginComponentTag from "./components/Login";
import allUsersComponentTag from "./components/all-users-component"
// import GoogleSignInButton from 'vue-google-signin-button-directive'
import { IonicVue } from "#ionic/vue";
/* Core CSS required for Ionic components to work properly */
import "#ionic/vue/css/core.css";
/* Basic CSS for apps built with Ionic */
import "#ionic/vue/css/normalize.css";
import "#ionic/vue/css/structure.css";
import "#ionic/vue/css/typography.css";
/* Optional CSS utils that can be commented out */
import "#ionic/vue/css/padding.css";
import "#ionic/vue/css/float-elements.css";
import "#ionic/vue/css/text-alignment.css";
import "#ionic/vue/css/text-transformation.css";
import "#ionic/vue/css/flex-utils.css";
import "#ionic/vue/css/display.css";
/* Theme variables */
import "./theme/variables.css";
const app = createApp(App)
.use(IonicVue)
.use(store)
.use(router)
app.component("LoginComponent", loginComponentTag);
app.component("AllUsersComponent",allUsersComponentTag)
router.isReady().then(() => {
app.mount("#app");
});
And my vue.config.js is settled in this way:
module.exports = {
devServer: {
proxy: "http://localhost:8100",
},
};
I checked my logs and eventually.
Literally, the situation is that despite having my state populated with data, unless I retrieve the whole state, I can't retrieve a particular item from it, cause the response is nothing.
Is there any configuration I'm omitting in this process I need to have in mind?

How to get the latest updated data on VueJs?

I'm new to Vuejs and still figuring out how to get the latest updated data.
app.js - preload the data after calling 'global/checkUserSignedIn'
import Vue from 'vue'
import axios from 'axios'
import router from './router'
import store from './store'
import { sync } from 'vuex-router-sync'
import App from 'components/app-root'
import { mapGetters, mapActions } from 'vuex'
Vue.prototype.$http = axios
sync(store, router)
const app = new Vue({
store,
router,
...App,
created() {
this.preload()
},
methods: {
preload: function () {
this.$store.dispatch('global/checkUserSignedIn')
}
}
})
export { app, router, store }
global.js (a vuex store)
/// actions, mutations here
const actions = {
checkUserSignedIn({ commit, state }) {
commonApi.isUserSignedIn().then(function (data) {
state.usersignedin = data
})
},
}
const getters = {
isUserSignedIn: state=>state.usersignedin
}
export default {
state,
getters,
actions,
mutations,
namespaced: true
}
On a component, I call:
export default {
computed: {
...mapGetters('global', ['isUserSignedIn']),
},
watch: {
isUserSignedIn: function (newVal, oldVal) {
if (newVal !== undefined) {
console.log('ok, correct result')
}
}
},
updated() {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
console.log('updated: ' + this.isUserSignedIn)
})
},
created() {
console.log('created :' this.isUserSignedIn)
}
}
}
On both Updated and Created, I don't get the last updated data isUserSignedIn which is True if a user has been logged in though if I put {{isUserSignedIn }} on a template, it shows correctly.
Is there any way to get it to work other than having to use a Watcher here.
Updated: Maybe using Watcher is the only way to get the lastest updated data.