Vuex state on page refresh
Asked Answered
S

8

178

My app uses the Firebase API for User Authentication, saving the Login status as a boolean value in a Vuex State.

When the user logs in I set the login status and conditionally display the Login/Logout button accordingly.

But when the​ page is refreshed, the state of the vue app is lost and reset to default

This causes a problem as even when the user is logged in and the page is refreshed the login status is set back to false and the login button is displayed instead of logout button even though the user stays logged in....

What shall I do to prevent this behavior

Shall I use cookies Or any other better solution is available...

    -
Stillhunt answered 26/3, 2017 at 10:19 Comment(4)
I use any kind of local storage to handle that. That can be Cookies or something elseGodroon
@El_Matella apart of cookies what else method do you use to store data locallyStillhunt
In general, I use a local storage npm package that can choose the best method to store data for me: npmjs.com/package/local-storage "The API is a simplified way to interact with all things localStorage. Note that when localStorage is unsupported in the current browser, a fallback to an in-memory store is used transparently."Godroon
@El_Matella thank you very much... I will have a lookStillhunt
F
156

This is a known use case. There are different solutions.

For example, one can use vuex-persistedstate. This is a plugin for vuex to handle and store state between page refreshes.

Sample code:

import { Store } from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import * as Cookies from 'js-cookie'

const store = new Store({
  // ...
  plugins: [
    createPersistedState({
      getState: (key) => Cookies.getJSON(key),
      setState: (key, state) => Cookies.set(key, state, { expires: 3, secure: true })
    })
  ]
})

What we do here is simple:

  1. you need to install js-cookie
  2. on getState we try to load saved state from Cookies
  3. on setState we save our state to Cookies

Docs and installation instructions: https://www.npmjs.com/package/vuex-persistedstate

Forepeak answered 26/3, 2017 at 10:34 Comment(7)
Thank you... Was just having look at the plugin's github page... Thank you once againStillhunt
Do you need to do anything specific to set / get the data ? On reload my data is reset to default. Just setting via this.$store.state.user, tried objects and simple strings - no luck.Seko
Because cookies are transmitted between client and server I would probably look at local storage instead ...Watersoak
how do I save the state of aws-amplify ? as it is to big to fit in cookies and localstorage won't work on safari private modePatchouli
@Patchouli I am also facing the same issue, found any solution for this?Coma
switched out to this methodology dev.to/dabit3/…Patchouli
This library is no longer supported (per page on npm), any work arounds or OK to use this (I suppose until some breaking changes happen on the Vue side)?Hovel
W
122

When creating your VueX state, save it to session storage using the vuex-persistedstate plugin. In this way, the information will be lost when the browser is closed. Avoid use of cookies as these values will travel between client and server.

import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'

Vue.use(Vuex);

export default new Vuex.Store({
    plugins: [createPersistedState({
        storage: window.sessionStorage,
    })],
    state: {
        //....
    }
});

Use sessionStorage.clear(); when user logs out manually.

EDIT: Note that if your store have values that are not intrinsically string types (eg dates), your application may fail or behaviour may change because the serialisation/deserialisation process will convert these values to strings.

Watersoak answered 13/11, 2019 at 16:21 Comment(8)
I'm surprised that the cookies solution gets so many stars. I think this solution is much better as it automatically clears all state when the browser window is closed. I don't like sending my state data as cookies to the server, and I also don't want to persist sensitive data when the browser window closes.Admittedly
You are also limited to 8k in total with your headers including cookies.Watersoak
@MarkHagers and it is natively supported since IE8! No need to load extra code.Orlina
I was getting an error vuex-persistedstate.es.js?0e44:1 Uncaught (in promise) TypeError: Converting circular structure to JSONClo
@Akin - The error suggests you have a circular reference in your state, an object references another object which eventually references back to the first object.Watersoak
Using Typescript I have an error on refresh : vuex-persistedstate.es.js?0e44:1 Uncaught TypeError: r.propertyIsEnumerable is not a functionInefficient
What if I want the browser to remember that user instead of login everytime?Sartor
@Sartor I think that deserves a separate questionWatersoak
E
37

Vuex state is kept in memory. Page load will purge this current state. This is why the state does not persist on reload.

But the vuex-persistedstate plugin solves this issue

npm install --save vuex-persistedstate

Now import this into the store.

import Vue from 'vue'
import Vuex from 'vuex'
import account from './modules/account'
import createPersistedState from "vuex-persistedstate";

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    account,
  },
  plugins: [createPersistedState()]
});

It worked perfectly with a single line of code: plugins: [createPersistedState()]

Elwandaelwee answered 28/3, 2020 at 10:50 Comment(4)
with vuex create store: const store = createStore({ /*...*/ plugins: [createPersistedState()], });Galatians
this is the simplest approach without adding another package. It works!Cyclohexane
Works great! By default state is saved to localStorage under the vuex key.Leath
Extremely easy to integrate .Jim
K
18

put on state:

producer: JSON.parse(localStorage.getItem('producer') || "{}")

put on mutations:

localStorage.setItem("producer",JSON.stringify(state.producer)) // OR
localStorage.removeItem("producers");

works fine for me!

Kiangsu answered 30/11, 2018 at 20:22 Comment(3)
Great answer consider it requires no extra libraries and persiststate is no longer being supportedHovel
nice one but I think the localStorage.removeItem("producer"); should come before the localStorage.setItem("producer",JSON.stringify(state.producer))Votive
well... he could be writing janky voting poll software that aims to remove any new votes before they are counted?Rolph
M
14

I think use cookies/localStorage to save login status might cause some error in some situation.
Firebase already record login information at localStorage for us include expirationTime and refreshToken.
Therefore I will use Vue created hook and Firebase api to check login status.
If token was expired, the api will refresh token for us.
So we can make sure the login status display in our app is equal to Firebase.

new Vue({
    el: '#app',
    created() {
        firebase.auth().onAuthStateChanged((user) => {
            if (user) {
                log('User is logined');
                // update data or vuex state
            } else {
                log('User is not logged in.');
            }
        });
    },
});
Major answered 30/6, 2017 at 15:14 Comment(2)
the best, official and recommended approach against this situationDelacruz
this is the right answer to the original question regarding login status for other, less secretive data Leonardo's answer stackoverflow.com/users/631517/leonardo-filipe is the best option - use localStorage - its already there and super simpleRolph
E
4

I've solved this by resetting my headers every time I re-load also fetch user data, I don't know what is better ...

new Vue({
    el: 'vue',
    render: h => h(VueBox),
    router,
    store,

    computed: {
        tokenJWT () {
            return this.$store.getters.tokenCheck
        },
    },


    created() {
        this.setAuth()

    },

    methods:
        Object.assign({}, mapActions(['setUser']), {

            setAuth(){
                if (this.tokenJWT) {
                    if (this.tokenJWT === 'expired') {
                        this.$store.dispatch('destroyAuth')
                        this.$store.dispatch('openModal')
                        this.$store.dispatch('setElModal', 'form-login')

                    } else {
                        window.axios.defaults.headers.common = {
                            'Accept': 'application/json',
                            'Authorization': 'Bearer ' + this.tokenJWT
                        }
                        axios.get( api.domain + api.authApi )
                            .then(res => {
                                if (res.status == 200) {
                                    this.setUser( res.data.user )
                                }
                            })
                            .catch( errors => {
                                console.log(errors)
                                this.destroyAuth()
                            })
                    }
                }
            }
        })

})
Eo answered 27/6, 2017 at 16:23 Comment(0)
L
2

Here's how to do it with the composition api.

npm install --save vuex-persistedstate

import createPersistedState from "vuex-persistedstate";
import { createStore } from "vuex";

export default createStore({
  state: {
    project_id: 1,
  },
  mutations: {
    setProjectId(state, payload) {
      state.project_id = payload;
    },
  },
  getters: {},
  actions: {},
  plugins: [createPersistedState()],
});
Leduc answered 7/10, 2021 at 16:19 Comment(1)
vuex-persistedstate is not deprecatedClaudy
O
0

In this case, the easiest way is installing npm install --save vuex-persistedstate
Then, Importing it into your vuex file import createPersistedState from "vuex-persistedstate"; Then, just put it in your createStore like:

plugins: [createPersistedState()],
    state(){
        return{
            isActive: false,
        }
    }
Orin answered 31/8, 2021 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.