I developing nuxt.js app. And point is login & logout.
We will develop a login to the JWT system.
You must remain logged in at vuex.
However, when I refresh the page, vuex is initialized.
I've read git vuex-persistedstate , but it's hard to understand just how to initialize and set it.
What is the best way to develop a login system in nuxt.js?
Thanks.
Using vuex-persisted state would be the best use case for your scenario.
I will walk you through the process of using vuex-persisted state.
Open command line, cd to your project directory, then enter npm install --save vuex-persistedstate. This will install vuex-persistedstate into your project dependencoes.
Now in your store.js file or wherever your defined your vuex store, add the vuex-persistedstate plugin
import createPersistedState from "vuex-persistedstate";
import * as Cookie from "js-cookie";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
user: {
name: "john doe",
age: " 16",
},
loggedIn: false,
hobbies: ["eating", "partying"],
},
plugins: [
createPersistedState({
paths: ["user", "loggedIn"],
getState: (key) => Cookie.getJSON(key),
setState: (key, state) =>
Cookie.set(key, state, { expires: 1, secure: false }),
}),
],
});
You also need js-cookie package which makes handling cookies easier. Use npm install --save js-cookie.
The paths property says which parts of the state to persist, in our case save as cookies.If no path property is given, then the whole state is persisted
From the above example we have mentioned the paths paths: ['user', 'loggedIn'], so only user and loggedIn properties of the state are saved in cookies not hobbies.
In case you are using modules in your store, the way of defining the pats to persist would be as follows:
import createPersistedState from "vuex-persistedstate";
import * as Cookie from "js-cookie";
import myModule from "./myModule";
import myAnotherModule from "./myAnotherModule";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
user: {
name: "john doe",
age: " 16",
},
loggedIn: false,
hobbies: ["eating", "partying"],
},
modules: {
myModule,
myAnotherModule,
},
plugins: [
createPersistedState({
paths: ["user", "loggedIn", "myModule.<nameOfThePropretyInState>"],
getState: (key) => Cookie.getJSON(key),
setState: (key, state) =>
Cookie.set(key, state, { expires: 1, secure: false }),
}),
],
});
In your paths you will refer to the module's property in the state you want to persist. In the above example, the property of the state that you mention of myModule is persisted. myAnotherModule state is not saved since it is not mentioned in the paths.
That's it . If you want to customize the way you use vuex-persisted state and js-cookie, have a look at their documentation.
If you want to check whether your desired state is saved in cookies then you can console log your cookies like this: console.log(document.cookie in your App.vue created() lifecycle hook
I have used vuex-persist package instead, very easy to get it up and running. This works for SSR too.
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersist from 'vuex-persist'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
Vue.use(Vuex)
let vuexLocalStorage = null;
if (process.browser) {
vuexLocalStorage = new VuexPersist({
key: 'vuex', // The key to store the state on in the storage provider.
storage: window.localStorage, // or window.sessionStorage or localForage
})
}
export function createStore() {
return new Vuex.Store({
state: {
},
actions,
mutations,
getters,
plugins: process.browser ? [vuexLocalStorage.plugin] : []
})
}
Just make sure to condition everything to just run in the browser
Better to use cookies for saving authorization token, look at this nuxt module
https://github.com/microcipcip/cookie-universal/tree/master/packages/cookie-universal-nuxt
Here sample on vuex store module to set cookie
//call async ajax request to get UUID
const uuidReq = await dispatch('getUUID')
if (uuidReq.hasOwnProperty('meta')) {
commit('setState', {
uuid: uuidReq.meta.links.me.meta.id,
isLogin: true
})
// calculate expires
const expDate = new Date()
expDate.setTime(expDate.getTime() + (state.accExpKey - 0.3) * 1000)
const expDate2 = new Date()
expDate2.setTime(expDate.getTime() + 2592000 * 1000)
const options = {
path: '/',
expires: expDate
}
const options2 = {
path: '/',
expires: expDate2
}
const cookieList = [{
name: 'g_isLogin',
value: true,
opts: options2
},
{
name: 'g_accKey',
value: state.accKey,
opts: options
},
{
name: 'g_refKey',
value: state.refKey,
opts: options2
},
{
name: 'g_userUUID',
value: uuidReq.meta.links.me.meta.id,
opts: options
}
]
this.$cookies.setAll(cookieList)
}
Here sample implementation on custom Nuxt middleware check existing cookie then inject them into vuex state
export default function({ store, route, redirect, app }) {
const isLogin = app.$cookies.get('g_isLogin') === 'true'
const accKey = app.$cookies.get('g_accKey') || ''
const refKey = app.$cookies.get('g_refKey') || ''
const userUUID = app.$cookies.get('g_userUUID') || ''
// console.warn('authenticated isLogin:', isLogin)
// If the user authenticated
if (isLogin) {
store.commit('user/setState', {
isLogin: isLogin,
accKey: accKey,
refKey: refKey,
uuid: userUUID
})
} else {
return redirect('/?prevURL=' + route.path)
}
}
I would strongly recommend using cookies over localStorage with nuxt and the vuex store. Using a package such as univeral-cookie and the built-in nuxtServerInit action, you can populate both client and server stores by reading the cookies on the initial request from the server. You may be limited in the amount of data you can store with cookies but if you implement a RESTful-like API and store ids in your cookies whenever possible, you can server-side fetch that data to populate the full stack store thereby setting yourself up very well in cases where the user refreshes the page. I found it very handy with auth tokens, too, which expire on their own cookie-related behavior and hence wont exist in the store (or its mutation handled decoded data) in cases where the page refreshes.
for using vuex-persistedstate in nuxt both client and server , follow these steps.
For example consider you have a Vuex Module user and you want to persist it . even if you refresh or route to another page.
const user = {
namespaced: true,
state: () => ({
name: 'geeekfa'
}),
mutations: {
name(state, name) {
state.name = name;
},
},
getters: {
name: (state) => {
return state.name;
},
}
}
export default user
install vuex-persistedstate
npm install --save vuex-persistedstate
install cookie & js-cookie
npm install --save cookie js-cookie
after that your package.json is like :
"dependencies": {
...
"cookie": "^0.3.1",
"js-cookie": "^2.2.1",
"vuex-persistedstate": "^4.0.0-beta.3",
...
}
create persistedState.js in ~/plugin/persistedState.js
// persistedState.js
import createPersistedState from 'vuex-persistedstate'
import * as Cookies from 'js-cookie'
import cookie from 'cookie'
export default ({ store, req }) => {
createPersistedState({
paths: ['user'], // your vuex module name
storage: {
getItem: (key) => {
if (process.server) {
const parsedCookies = cookie.parse(req.headers.cookie)
return parsedCookies[key]
} else {
return Cookies.get(key)
}
},
setItem: (key, value) =>
Cookies.set(key, value, { expires: 365, secure: false }),
removeItem: key => Cookies.remove(key)
}
})(store)
}
add this plugin to nuxt.config.js
plugins: [
...
{ src: '~/plugins/persistedState.js' }
...
],
this is enough ! you can persist user module even after refresh in both client and server side . there is no need to change ~/store/index.js file
Related
My goal is to commit (invoke/call) a mutation that I've defined in my Vuex store.
store/store.js
export default {
modules: {
app: {
state: {
shouldDoThing: false,
}
mutations: {
setShouldDoThing: (state, doThing) => { state.shouldDoThing = doThing },
}
}
}
}
Since I attach Vuex to my app, I can use this.$store.commit throughout the app in various components without issue.
main.js
import Store from 'store/store.js';
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const app = new Vue({
el: '#app-root',
store,
// ...etc
});
For example:
exampleComponent.vue
export default {
created() {
// This works!
this.$store.commit('setShouldDoThing', true);
},
}
Now I'd like to commit something from a vue-router Route file, in a beforeEnter method:
exampleRoute.js
import Store from 'store/store.js'
const someRoute = {
path: '/blah',
beforeEnter(to, from, next) {
Store.commit('setShouldDoThing', true);
next();
}
}
However, when I try the above, I get the error
TypeError: _state_store__WEBPACK_IMPORTED_MODULE_10__.default.commit is not a function
There's lots of examples online of successfully using vuex getters by importing. And, if I console.log() the Store import, I can see my entire store structure
modules:
app:
actions: {someAction: ƒ, …}
getters: {isStartingQuery: ƒ}
mutations: {ariaAnnounce: ƒ, …}
state: {…}
__proto__: Object
How can I import my Store and then commit a mutation from within a vue-router file?
I've been googling for a very long time, and didn't find a stackoverflow answer or a vue forums answer for this specific case or issue, so below is the solution that I tested and works in my case.
For whatever reason, I can't trigger commit. However, I can simply invoke the mutation directly, and this change is then reflected throughout other components (as in, a "different" store wasn't imported).
someRoute.js
import Store from 'store/store.js'
const someRoute = {
path: '/blah',
beforeEnter(to, from, next) {
Store.modules.app.mutations.setShouldDoThing(Store.modules.app.state, true);
next();
}
}
And later, in some component:
someComponent.vue
export default {
beforeMount() {
console.log(this.$store.state.app.shouldDoThing);
// true
}
}
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.
I've added vuex-persistedstate as defined in documentation. (confirmed and working)
export default new Vuex.Store({
modules: {
auth,
},
plugins: [VuexPersistedState()]
});
I've set up a router navigation guard to redirect to home page on login
/* Auth based route handler */
router.beforeEach((to, from, next) => {
if (to.meta.hasOwnProperty('requiresAuth')) {
if (to.meta.requiresAuth === true) {
let isAuthenticated = authStore.getters.isAuthenticated
if (isAuthenticated(authStore.state) === true) {
next()
} else {
next({name: 'login'})
}
} else {
let isAuthenticated = authStore.getters.isAuthenticated
console.log(authStore.state)
if (isAuthenticated(authStore.state) === true) {
next({name: 'home'})
} else {
next()
}
}
} else {
next()
}
})
The vuex persistedstate restores store from local storage but not before navigation guard!
I can provide any necessary part of the source code for evaluation. Please comment your request as needed. Experimental solutions are also welcome. This is just a personal training application for me!
Help is appreciated!
I know this is probably of no use to #Vaishnav, but as I wen't down the same rabbit hole recently I figured I'd post a workaround I found for this here as this was the only post I found that asked this issue specifically.
in your store/index.js You need to export both the function and the store object.
Change this :
export default() => {
return new vuex.Store({
namespaced: true,
strict: debug,
modules: {
someMod
},
plugins: [createPersistedState(persistedStateConfig)]
});
};
to:
const storeObj = new vuex.Store({
namespaced: true,
strict: debug,
modules: {
someMod
},
plugins: [createPersistedState(persistedStateConfig)]
});
const store = () => {
return storeObj;
};
export {store, storeObj}
Then also, as you have now changed the way you export the store you will also need to change the way it's imported.
Everywhere in your app you've imported the store ie: in main.js -> import store from '#/store' excluding the router.js you will need to change to import {store} from '#/store'
And in your router.js just import {storeObj} from '#/store' and use that instead of store in your router guard ie: storeObj.getters['someMod/someValue']
I found a working example.
As the router isn't a component.
In router config file -
import authStore from '#/stores/auth/store'
Instead -
import store from '#/store'
in navigation guard, I replaced
let isAuthenticated = authStore.getters.isAuthenticated
if(isAuthenticated(authstore.state) === true){...}
with -
let isAuthenticated = store.getters['auth/isAuthenticated']
if(isAuthenticated === true){...}
And now this works like charm...
I use vuex-persistedstate package (https://github.com/robinvdvleuten/vuex-persistedstate) to persist data state on browser.
I use Adonuxt (a mix between NuxtJS and AdonisJS).
In VueX actions, I have this action:
nuxtClientInit ({commit}) {
// I want get here state auth saved by persistedstate package
}
This action is called by plugin:
localstorage.js
export default async (context) => {
await context.store.dispatch('nuxtClientInit', context)
}
nuxt.js plugin (config)
{
src: '~/plugins/localstorage.js',
ssr: false
}
I want get state to configure Axios with the user token:
this.$axios.setToken(auth.jwt.token, 'Bearer')
I have the impression nuxtClientInit() is called before persistedstate package, so state.auth is null but it can observable in console:
I've used https://www.npmjs.com/package/vuex-persist to persist data from Vuex.
csr+ssr cookie
You can choose any one of the below library
1 .vuex-persistedstate
2 .vuex-persist
vuex-persistedstate usage
https://www.npmjs.com/package/vuex-persistedstate
plugins/persistedstate.js
import createPersistedState from 'vuex-persistedstate'
import * as Cookies from 'js-cookie'
import cookie from 'cookie'
export default ({store, req, isDev}) => {
createPersistedState({
key: 'your_key',
paths: ['state1', 'state2',...so_on],
storage: {
getItem: (key) => process.client ? Cookies.getJSON(key) : cookie.parse(req.headers.cookie||'')[key],
setItem: (key, value) => Cookies.set(key, value, { expires: 365, secure: !isDev }),
removeItem: (key) => Cookies.remove(key)
}
})(store)
}
nuxt.config.js
plugins: [
{ src: '~plugins/persistedstate.js' }
]
vuex-persist
https://www.npmjs.com/package/vuex-persist
// ~/plugins/vuex-persist.js
import * as Cookies from 'js-cookie'
import cookie from 'cookie'
import VuexPersistence from 'vuex-persist'
export default ({ store, req, isDev }) => {
new VuexPersistence({
key:'test',
reducer: (state) => ({}),
restoreState: (key, storage) =>process.client ? Cookies.getJSON(key) : cookie.parse(req.headers.cookie||'')[key],
saveState: (key, state, storage) =>
Cookies.set(key, value, { expires: 365, secure: !isDev }),
}).plugin(store);
}
nuxt.config.js
plugins: [
{ src: '~plugins/vuex-persist.js' }
]
In my case, I made a mistake in specifying the directory.
root/
├ src/
├ pages/
.
.
├ src/
└ plugins/
└ localstorage.js/
In the above directory, you must specify as follows.
In nuxt.config.js
{src:'~/src/plugins/localstorage.js', srr: false}
I recently was struggling with implementing modules in Vuex for the first time. I couldn't find much info on the console error message I was getting ( rawModule is undefined ), so I thought I'd share the issue I ran into and the solution. I was doing a quick, simple version of a module implementation as I was working through some examples:
export const store = new Vuex.Store({
state: {
loggedIn: false,
user: {},
destination: ''
},
mutations: {
login: state => state.loggedIn = true,
logout: state => state.loggedIn = false,
updateUser: ( state, user ) => { state.user = user },
updateDestination: ( state, newPath ) => { state.destination = newPath }
},
modules: {
project
},
});
const project = {
state: {}
}
The issue ultimately was that I had declared my module after I tried to add it to the Vuex store. I had thought it would have been okay to declare the module later thanks to variable hoisting, but that doesn't appear to be the case. Here is the code that does work:
const project = {
state: {}
}
export const store = new Vuex.Store({
state: {
loggedIn: false,
user: {},
destination: ''
},
mutations: {
login: state => state.loggedIn = true,
logout: state => state.loggedIn = false,
updateUser: ( state, user ) => { state.user = user },
updateDestination: ( state, newPath ) => { state.destination = newPath }
},
modules: {
project
},
});
Hopefully this saves some people some time. I didn't see anything in the documentation requiring a certain ordering, so I'm surprised it mattered. If anyone has some insight into why it works this way, I'd be really interested in hearing it! Perhaps because the Vuex.Store() function gets called before the project value is set, so the project module's value is encapsulated as undefined, and that causes the error?
If you have using class components, put the store import before the module import, Eg:
// This is wrong
import { getModule } from "vuex-module-decorators";
import AppModule from "#/store/app-module";
import store from "#/store";
const appModule = getModule(AppState, store);
// This work
import { getModule } from "vuex-module-decorators";
import store from "#/store"; // Before first
import AppModule from "#/store/app-module"; // Then import the module
const appModule = getModule(AppState, store);