DEV Community

Nick Shulhin
Nick Shulhin

Posted on

🔥Persistent store with VueJS, VueX and VueX Persisted State 🔥

Sometimes we need our VueJS Web App to persist some information in browser local storage. It could be local settings, account info or some tokens. We definitely don’t want to lose them once the page is refreshed.

In this quick example, I’ll show how to use vuex and vuex-persistedstate to make the magic happen.

Let’s start with required libraries!

Assuming you already generated your VueJS project, we will need an extra dependency, which is a vuex state management library:

npm install --save vuex

Our application at start should look something like this:

├── index.html
├── main.js
├── components
│   ├── App.vue

To start creating a store, we need to create a store directory on the same level as components and main.js.

Then, inside newly-created store directory we will create index.js - which will be a reference entry point for our store configuration.

Inside store/index.js file we are going to create a store and export it to be used by our application:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({
});

export default store

That’s it! We created our store!

Now your project should be like this:

├── index.html
├── main.js
├── components
│   ├── App.vue
└── store
    ├── index.js

Next step is to make our store available globally, for this purpose it is enough to reference it inside main.js file just like this:

import Vue from 'vue'
import App from './components/App'
import store from './store';

Vue.config.productionTip = false

new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App }
})

Amazing! But what about actual logic?

Now let’s imagine we need to store some User ID information. For our basic functionality we will have login (which will set User ID) and logout (which will set User ID to null).

Firstly, let’s create modules directory in our store folder. The main logic behind modules is to decouple store according to some context. Otherwise we can end up with a single file and super-mega-big unmanageable store.

Inside modules now it is a time to create account.js with all required logic:

const state = {
  userId: null
};

const mutations = {
  logout(state) {
    state.userId = null;
  },
  login(state, userId) {
    state.userId = userId;
  }
};

export default {
  state,
  mutations
}

So, what's happening here?

Firstly, we declare our store property with a single variable, which is userId. By default it is null.

Next, we have something which is called mutations with two simple functions: login and logout. Main idea behind vuex is that we do not directly mutate a store, but commit our changes. Basically, these functions will be call on commit only.

Our project should look like this:

├── index.html
├── main.js
├── components
│   ├── App.vue
└── store
    ├── index.js
    ├── modules
         ├── account.js

But how would store know about our newly-created account.js? We need to declare it!

Inside our store/index.js let's do it:

import Vue from 'vue'
import Vuex from 'vuex'
import account from './modules/account'

Vue.use(Vuex);

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

export default store

As you can see, we simply imported our account.js and referenced it inside modules for the store. Easy!

It is a time to try our amazing store!

Inside our App.vue:

<template>
  <div>
    <div>{{this.userId}}</div>
    <button @click="login()">Login as Nick</button>
    <button @click="logout()">Logout</button>
  </div>
</template>

<script>
    import { mapState } from 'vuex'

    export default {
        name: 'app',
        computed: mapState({
            userId: state => state.account.userId
        }),
        methods: {
            login() {
                this.$store.commit('login', 'Nick')
            },
            logout() {
                this.$store.commit('logout')
            }
        },
    }
</script>

In this example, we are using mapState from vuex inside computed vue property which allows us to translate global state into our local store properties. If anything changed in global store, it will be also changed in our component properties. As a result we can simply use this.userId, which is directly mapped from global store of our account context.

Next, we are using two methods: login and logout. As you can see now, to modify our store we commit a change, specified earlier in account mutations property.

Awesome! We made it!

But... if you reload a page...

Yes, unfortunately it doesn't persist.

If you open Application/Local Storage in Chrome, there will be no trace of our userId.

But there is a solution called vuex-persistedstate!

Let's install a plugin which allows our state to persist on local storage:

npm install --save vuex-persistedstate

For our vanilla configuration it is enough to reference it as a plugin inside store/index.js as follows:

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()]
});

export default store

Now if you reload a page once logged in - it will be persisted in local storage on your browser!

Good job, you made it! Now your app can truly store things!

For more references, go to here => https://vuex.vuejs.org/

alt text

Top comments (8)

Collapse
 
efraimla profile image
Efraim

How can i do to only persist an state module?

Collapse
 
tojen profile image
Tomisin
Collapse
 
roshnet profile image
Roshan Sharma

Thanks, this was helpful!

Collapse
 
nickitax profile image
Nick Shulhin

Thanks! 👌👌👌

Collapse
 
ahmetilhn profile image
Ahmet İlhan
Collapse
 
peterf2170 profile image
PeterF2170

Thanks 🥳 exactly the answer I've been searching for a week 😭😂

Collapse
 
marchev profile image
Martin Marchev

Great tutorial! :)

Collapse
 
philayoav profile image
Yoav Morahg

Great simple explanation of Vuex and how to solve that pesky “lose-store-on-reload” problem. Thanks!!!