robinvdvleuten / vuex-persistedstate

💾 Persist and rehydrate your Vuex state between page reloads.
https://npm.im/vuex-persistedstate
MIT License
5.76k stars 378 forks source link

Delete persisted state #88

Closed zagreusinoz closed 6 years ago

zagreusinoz commented 6 years ago

Do you want to request a feature or report a bug? Feature

What is the current behavior? There is no way to delete the persisted state programmatically, there are use cases for removing the state in local storage.

If the current behavior is a bug, please provide the steps to reproduce.

What is the expected behavior? We should be able to remove the state from local storage either with localStorage.removeItem('vuex') or something like this.$store.clear.

If this is a feature request, what is motivation or use case for changing the behavior?

When my users log out I want to remove all of the state data, this way when a different person logs in none of the existing data is shared.

I tried to do this with localStorage.removeItem('vuex') however I see that in #37 it's commented that the plugin gets there first and the state is rewritten again.

It's possible to do it manually, however this would mean setting all of the states to default values in all of the modules as far as I can tell which is not very efficient.

robinvdvleuten commented 6 years ago

The recommended way here is to stay away from the localStorage entry. If you would like the remove any "user" related state, you can do that with a mutation on your state. This keeps all state related issues to a bare minimum in your application.

TitanFighter commented 6 years ago

After deep digging in internet I found a clean solution which works well.

Based on these 2 answers (#1 #2) I made a workable code.

My structure of Vuex's index.js:

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

import { header } from './header'
import { media } from './media'

Vue.use(Vuex)

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

  modules: {
    header,
    media
  }
})

export default store

Inside each module we need to move all states into separated var initialState and in mutation define a function resetState, like below for media.js:

const initialState = () => ({
  stateOne: 0,

  stateTwo: {
    isImportedSelected: false,
    isImportedIndeterminate: false,

    isImportedMaximized: false,
    isImportedSortedAsc: false,

    items: [],

  stateN: ...
  }
})

export const media = {
  namespaced: true,

  state: initialState, // <<---- Our States

  getters: {
  },

  actions: {
  },

  mutations: {
    resetState (state) {
      const initial = initialState()
      Object.keys(initial).forEach(key => { state[key] = initial[key] })
    },
  }

}

In Vue component we can use it like:

<template>
</template>

<script>
  import { mapMutations } from 'vuex'

  export default {
    name: 'SomeName',

    data () {
      return {
        dataOne: '',
        dataTwo: 2
      }
    },

    computed: {
    },

    methods: {
      ...mapMutations('media', [ // <<---- define module
        'resetState' // <<---- define mutation
      ]),

      logout () {
        this.resetState() // <<---- use mutation
        // ... any code if you need to do something here
      }
    },

    mounted () {
    }
  } // End of 'default'

</script>

<style>
</style>
AlexeyKot commented 5 years ago

Thanks! But if I have lots of modules, do I need to call such reset methods for all of my modules inside logout() method (which is also is a method inside auth module)?

vkatsar commented 5 years ago

Hello everybody,

I think its better to use the reducer method to remove all the vuex state.

createPersistedState({
    key: 'vuex',
    reducer (val) {
      if(val.user.token === null) { // val.user.token (your user token for example)
        return {}
      }
      return val
    }
  })(store)
tcamde commented 4 years ago

@vkatsar I implemented your solution, thanks for that!

Just two remarks for other people trying this. In my setup I had to remove the "(store)" to make it work. Also it's important to know that the state will only be removed after a page refresh, so my logout action looks like this:

Action:

async logout ({ commit }) {    
    await AuthenticationApi.logout()  // Only in my setup: async call to server invalidate token on server-side
    commit('logout') 
    location.reload()  
  }

Mutation:

  logout: (state) => {
    state.token = null // Only in my setup remove session token
    state.isUserLoggedIn = false // this will trigger the vue-persist-store plugin to set all state to empty on the location relaod

  },

and the reducer:

createPersistedState({
              key: 'vuex',              
              reducer (val) {                                
                if(val.authentication.isUserLoggedIn === false) { // return empty state when user logged out                
                  return {}
                }
                return val
              }
            })
kasperkamperman commented 4 years ago

@tcamde how does you "logOutUser" look? Is this needed? It seems that the answer is not complete.

commit('logOutUser') // this will cause the vue-persist-store plugin to set all state to empty

xlcrr commented 4 years ago

I was able to get this working with the reducer method and

axios.get('logout')
  .then(resp => {
       window.location.href = window.location.href;
       this.$store.commit('logout)';
   });

This thread should probably be posted on the readme

uxinkc commented 4 years ago

Can someone please provide a full example here of how to delete the persisted state. All of the above make reference to code that isn't visible to the Reader, so there is no way to connect the dots and understand how to actually use reducer.

For example, what is val in the above reducer examples and how is that called when a User clicks a logout button? How would calling a mutation to "logout" clear the persisted data? Some context in examples goes a very long way, and a full solution with all relevant code helps even more.

tcamde commented 4 years ago

@uxinkc @kasperkamperman

I updated my example with the full code, though it might be a bit specific to my use-case

uxinkc commented 4 years ago

@tcamde I appreciate the update!

had a couple more questions for you...

  1. What does val represent in your example below and how is the reducer called?

    reducer(val){
    if(val.authentication.isUserLoggedIn === false) ...
    }
  2. Is authentication in above the name of the store module that has the isUserLoggedIn state property?

ttntm commented 4 years ago

Thanks @tcamde, this really helped:

reducer (val) {
  if(!val.user.currentUser) { // return empty state when user logged out
    return {}
  }
  return val
},

if(!val.user.currentUser) needs to point to the state object's key one stores the "user is logged in" token/value/... in.

In my case, state.currentUser is initialized as null in my store's user.js module. The reducer's parameter val seems to represent the whole "persisted state" as such, making it possible to access state values just like you would from inside your app with this.$store.state.moduleName.stateKeyName.

So, to answer @uxinkc's second question: yes, authentication is a store module's name and in this module there's that state property.

lvsong77 commented 3 years ago

@tcamde

Sorry to bother you, but is it necessary to refresh the page to logout? I have the same problem, but 'location.reload' will lead to a while of blank white screen in front of user. Is there any better plan by now?

KimZing commented 3 years ago

Hello everybody,

I think its better to use the reducer method to remove all the vuex state.

createPersistedState({
    key: 'vuex',
    reducer (val) {
      if(val.user.token === null) { // val.user.token (your user token for example)
        return {}
      }
      return val
    }
  })(store)

I try the way to reset state in vuex modules when user logout, but other feature of my program not work。 so i used the following code, it works well

createPersistedState({
      key: 'any-key',
      storage: {
        setItem: (key, value) => {
          // parse value
          const state = JSON.parse(value)
          // if token not exist,clean storage
          if (state.user.token === null) {
            window.localStorage.clear()
            return
          }
          window.localStorage.setItem(key, value)
        },
        getItem: window.localStorage.getItem,
        removeItem: window.localStorage.removeItem
      }
    })
osmelg commented 2 years ago

@vkatsar I implemented your solution, thanks for that!

Just two remarks for other people trying this. In my setup I had to remove the "(store)" to make it work. Also it's important to know that the state will only be removed after a page refresh, so my logout action looks like this:

Action:

async logout ({ commit }) {    
    await AuthenticationApi.logout()  // Only in my setup: async call to server invalidate token on server-side
    commit('logout') 
    location.reload()  
  }

Mutation:

  logout: (state) => {
    state.token = null // Only in my setup remove session token
    state.isUserLoggedIn = false // this will trigger the vue-persist-store plugin to set all state to empty on the location relaod

  },

and the reducer:

createPersistedState({
              key: 'vuex',              
              reducer (val) {                                
                if(val.authentication.isUserLoggedIn === false) { // return empty state when user logged out                
                  return {}
                }
                return val
              }
            })

best option available out there...