because reloading the page deletes the vuex data, having used mutations, vuejs? - vuejs2

Currently I have the following configuration in my vuex, what I do is through the userAuth action verify the user's role and assign it to a property of the state 'rol_user', using the UPDATEROL mutation. I execute this action when I log in so that the user's role is recorded in vuex.
here the vuex configuration:
import Vue from "vue";
import Vuex from "vuex";
import firebase from 'firebase';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
rol_user: ''
},
mutations: {
UPDATEROL: function (state, payload) {
state.rol_user = payload.rol_user;
}
},
actions: {
userAuth: async (context) =>{
let user = firebase.auth().currentUser;
if(user){
let user_info = await firebase.database().ref('users').child(user.uid).once('value');
let val_user = user_info.val();
console.log("USER AUTH - VUEX");
console.log(val_user.rol);
context.commit('UPDATEROL',{
rol_user: val_user.rol
});
}
}
}
});
It effectively assigns the user's role to the state property, rol_user.
The problem I have is when reloading the page. When I reload the page rol_user returns to its initial state, that is empty.
How can I do so that the value of the role is not lost even when reloading the page, but rather that it is changed to empty only until I log out

You need to use some sort of storage mechanism and check that storage everytime the app is first mounted. For example, you could store a session key in localStorage or just store the user state that you want to persist.
Note, I do not know what kind of data you are storing, I am assuming rol_user is an object, if it is a string, you do not need JSON serialization/deserialization as I've done in the example below:
import Vue from "vue";
import Vuex from "vuex";
import firebase from 'firebase';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
rol_user: ''
},
mutations: {
UPDATEROL: function (state, payload) {
state.rol_user = payload.rol_user;
localStorage && (localStorage.rol_user = JSON.stringify(state.rol_user));
//^ try to store the user role in localStorage
},
},
actions: {
userAuth: async (context) =>{
if(localStorage && localStorage.rol_user) {
//^ if it's in localStorage, log the user back in/update state to match what it is in localStorage
context.commit('UPDATEROL', { rol_user: JSON.parse(localStorage.rol_user)});
return;
}
let user = firebase.auth().currentUser;
if(user){
let user_info = await firebase.database().ref('users').child(user.uid).once('value');
let val_user = user_info.val();
console.log("USER AUTH - VUEX");
console.log(val_user.rol);
context.commit('UPDATEROL',{
rol_user: val_user.rol
});
}
}
}
});
You could also use Cookies (cookies are generally more common for this purpose, but localStorage also works just fine)

Related

pinia store installed after state call

I'm building a vuejs 3 application with composition API.
I have 2 stores: a userStore for holding userid, jwt and similar stuff (that gets populated upon login) and a dataStore that holds data related to the user (populated when user does operations).
When a user logs in successfully, she is redirected to a page containing user data.
The login page uses the userStore and the data page uses the dataStore. The dataStore needs the user's id and jwt.
This method is called upon login:
const submitlogin = async () => {
try {
const response = await postData.post('/user/a/login', {
email: form.email,
password: form.password,
})
if (response) {
userStore.loggedIn = true
// first get the jwt
userStore.getJWT()
// then go to the next page where jwt is required
router.push({
name: 'operation',
params: { sens: 'depense', quand: 'maintenant' },
})
}
} catch (error) {
console.log (error)
}
}
I import the userStore into the dataStore:
// dataStore
import { defineStore } from 'pinia'
import { useUserStore } from '#/stores/userStore.js'
actions: {
async getAccounts(id, month, year) {
const user = useUserStore
// getData is an [axios create function][1]
getData.defaults.headers.common['__authorization__'] = user.jwt
getData.get(`/use/b/comptes/${id}/${month}/${year}`).then((response) => {
// cut because irrelevant here
}
Then, on the first after login:
// data view
import { useUserStore } from '../stores/userStore'
import { useDataStore } from '#/stores/dataStore'
const dataStore = useDataStore()
const userStore = useUserStore()
onMounted(() => {
dataStore.getAccounts()
})
However, the autorization header is undefined only at this first call. If I further navigated to other views where I import the dataStore user.jwt is defined.
It seems that the dataStore is mounted correclty, but its state isn't available yet at the moment I call it.
Solved!
I changed the dataStore so that userStore is defined not within the function, but right after import.
Kind of logical since the getAccounts function is async, so the definition of user.jwt also was.
import { defineStore } from 'pinia'
import { getData } from '#/composables/useApi'
import { sumBy } from 'lodash'
import { useUserStore } from '#/stores/userStore.js'
// put this here, not within the async action !
const userStore = useUserStore()
actions: {
async getAccounts(id, month, year) {
getData.defaults.headers.common['__authorization__'] = userStore.jwt
getData.get(`/use/b/comptes/${id}/${month}/${year}`).then((response) => {
// cut because irrelevant here
}

Auto Refresh for Vuex

I would like to implement a auto refresh feature for my VueX store.
Everything the user refresh their browser, an actions in VueX store will be triggered to load the user profile from API call.
Is't possible to achieve that?
import apiService from "#/services/apiService";
import apiUrls from "#/services/apiUrls";
import { getToken } from "#/services/jwtService";
// Code to run actions when user refresh
getToken() !== null ? this.actions.getUserProfile() : "";
const state = {
userProfile: {},
};
const getters = {
userProfile: (state) => state.userProfile,
};
const actions = {
async getUserProfile({ commit }) {
console.log("here");
try {
let response = await apiService.get(apiUrls.PROFILE);
commit("setUserProfile", response.data.data);
} catch (error) {
console.log(error);
}
},
};
Thank you.
A user refresh means that the application will be re-executed. So basically main.js will be re-executed, App.vue re-created, etc.
That means just have to call your code in main.js or in a created lifecycle hook of any top-level component.
By top-level component I means any component which is created early in the app

How to keep items in cart even after refresh using vuex

The problems i am facing are -
My state become empty when I refresh the page
same occurs when i logout and then login
I am new to vuex and I want to build a functioning cart for my e-commerce site.
My cart store is as follows (ps. I have only made a very simple outline to check if it is working as desired):
let dayCare = window.localStorage.getItem('dayCare');
const state = {
dayCare: dayCare ? JSON.parse(dayCare) : [],
};
const getters = {
dayCareItems (state){
return state.dayCare
}
};
const actions = {
dayCareCart({commit}, dayCare){
let data = dayCare
commit('pushDaycareToCart', data)
return data
commit('saveCart')
},
};
const mutations = {
pushDaycareToCart(state, data){
return state.dayCare.push(data)
},
saveCart(state) {
window.localStorage.setItem('dayCare', JSON.stringify(state.dayCare));
}
};
export default {
state,
actions,
mutations,
getters
};
the data for my cart comes from :
<button type="submit" #click="dayCareCart(cartData)">Add To Cart</button>
<script>
import {mapGetters, mapActions} from 'vuex'
export default {
methods: {
...mapActions(["dayCareCart"]),
}
}
</script>
I want to know what i should add to retain the data for the logged in user
Why do you expect it to persist data? You aren't using any kind of database to store the data.
Use the vuex plugin vuex-persist. You can use it to persist data to localstorage or cookies while using everything awesome bout vuex
I figured out how to persist the cart. The save mutation should be called in the push to cart mutation
let dayCare = window.localStorage.getItem('dayCare');
const state = {
dayCare: dayCare ? JSON.parse(dayCare) :[],
};
const getters = {
dayCareItems (state){
return state.dayCare
}
};
const actions = {
dayCareCart({commit}, dayCare){
commit('pushDaycareToCart', dayCare)
},
};
const mutations = {
pushDaycareToCart(state, dayCare){
state.dayCare.push(dayCare)
this.commit('saveCart')
},
saveCart(state) {
window.localStorage.setItem('dayCare', JSON.stringify(state.dayCare));
},
};
export default {
state,
actions,
mutations,
getters
};

Saving data to local storage using vuex persisted state

I'm currently using this plugin vuex-persistedstate
and I would like to use it with Vuex module of my Nuxt app.
Basically I have a login module if success, then store the authToken coming from the response to localStorage
Here's my code:
import axios from "axios";
import createPersistedState from 'vuex-persistedstate';
export const state = () => ({
signInAttrs: {
email: "",
password: ""
},
authToken: ""
});
export const mutations = {
SET_AUTH_TOKEN(state, token) {
state.authToken = token;
createPersistedState({
key: 'admin-auth-key',
paths: [],
reducer: state => ({
authToken: '123123123'
})
})(store);
}
};
export const actions = {
signInAdmin({ commit }, context) {
return axios.post(`${process.env.BASE_URL}/sign_in`, {
email: context.email,
password: context.password
}).then(response => {
commit('SET_AUTH_TOKEN', response.data.headers.token);
}).catch(error => {
console.log(`failed ${error}`);
});
}
};
export const getters = {
signInAttrs(state) {
return state.signInAttrs;
},
authToken(state) {
return state.authToken;
}
};
Inside the mutations there's SET_AUTH_TOKEN that receives the token as the parameter from API. How can I save it to localStorage?
I think you're misunderstanding usage of vuex-persistedstate. Once you add it to Store plugins (plugins: [createPersistedState()]), it automatically updates localStorage variable vuex with a copy of your store on each mutation commit (see example). So your token should be inside vuex.authToken in localStorage already.
If you want to simply store a variable with custom name you can do it without plugins: localStorage.setItem('key', 'value'). See this question.

Axios interceptor in vue 2 JS using vuex

I store token after success login call in vuex store like this:
axios.post('/api/auth/doLogin.php', params, axiosConfig)
.then(res => {
console.log(res.data); // token
this.$store.commit('login', res.data);
})
axiosConfig is file where I only set baseURL export default { baseURL: 'http://localhost/obiezaca/v2' } and params is just data sent to backend.
My vuex file looks is:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
logged: false,
token: ''
},
mutations: {
login: (state, response) => {
state.logged = true;
state.token = response;
console.log('state updated');
console.log('state.logged flag is: '+state.logged);
console.log('state.token: '+state.token);
},
logout: (state) => {
state.logged = false;
state.token = '';
}
}
});
It is working correctly, I can re-render some of content in my SPA basing on v-if="this.$store.state.logged" for logged user. I'm able to access this.$store.state.logged from any component in my entire app.
Now I want to add my token to every request which call my rest API backend. I've created basic axios http interceptor which looks like this:
import axios from 'axios';
axios.interceptors.request.use(function(config) {
const token = this.$store.state.token;
if(token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, function(err) {
return Promise.reject(err);
});
Now I have 2 problems/questions about it.
I know that it is available to use this.$store.state.logged or this.$store.state.token across every component but can I use it same way in single javascript file?
Where should I execute/start my interceptor javascript file? It is independent file which lays in my app main folder but I am not calling it anywhere, in angularJS which I was working before, I had to add $httpProvider.interceptors.push('authInterceptorService'); in config but I don't know how to do same thing in vue architecture. So where should I inject my interceptor?
EDIT
I followed GMaiolo tips I added
import interceptor from './helpers/httpInterceptor.js';
interceptor();
to my main.js file and I refactor my interceptor to this:
import axios from 'axios';
import store from '../store/store';
export default function execute() {
axios.interceptors.request.use(function(config) {
const token = this.$store.state.token;
if(token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, function(err) {
return Promise.reject(err);
});
}
Result of this changes is that every already existing backend calls ( GET ) which don't need token to work stopped working but it is logical because I didn't clarified to which request it should add token so it is trying to add it everywhere and in my interceptor something is still wrong and that is why every already exisitng request stopped working.
When I try to do backend POST call in browser console I still get this error:
TypeError: Cannot read property '$store' of undefined
Although I import store to my interceptor file. Any ideas? I can provide some more information if any needed.
I additionally add screenshot of this main, store and interceptor tree structure so you can see that I'm importing fron correct path:
1.
First of all I'd use a Vuex Module as this Login/Session behavior seems to be ideal for a Session module. After that (which is totally optional) you can set up a Getter to avoid accessing the state itself from outside Vuex, you'd would end up with something like this:
state: {
// bear in mind i'm not using a module here for the sake of simplicity
session: {
logged: false,
token: ''
}
},
getters: {
// could use only this getter and use it for both token and logged
session: state => state.session,
// or could have both getters separated
logged: state => state.session.logged,
token: state => state.session.token
},
mutations: {
...
}
With those getters set, you can get the values a bit easier from components. With either using this.$store.getters.logged (or the one you'd want to use) or using the mapGetters helper from Vuex [for more info about this you can check the getters docs]:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
...mapGetters([
'logged',
'token'
])
}
}
2.
I like to run Axios' interceptors along with Vue instantation in main.js creating, importing and executing an interceptors.js helper. I'd leave an example so you get an idea, but, then again, this is my own preference:
main.js
import Vue from 'vue';
import store from 'Src/store';
import router from 'Src/router';
import App from 'Src/App';
// importing the helper
import interceptorsSetup from 'Src/helpers/interceptors'
// and running it somewhere here
interceptorsSetup()
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
});
interceptors.js
import axios from 'axios';
import store from 'your/store/path/store'
export default function setup() {
axios.interceptors.request.use(function(config) {
const token = store.getters.token;
if(token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, function(err) {
return Promise.reject(err);
});
}
And there you'd end up having all the behavior cleanly encapsulated.
I did the same logic. however, I just change the file name. I used axios/index.js but the store is undefined there. so I just change the file name axios/interceptor.js and Don't know store data is accessible look at my below image