Ilyas Foo
25 September 2017
Offline persistence Vuex state with LocalForage

Vuex already has a couple of persistence plugins available, one of which is vuex-persistedstate. LocalForage API however, is fully asynchronous and could not be simply be drop-in as the storage provider. A workaround then is to simply save state on change and during app initialize, we will load back the saved state in an async function.

Store plugin

A Vuex store plugin will provide a callback which will receive every change of state through Vuex mutation.

const persistPlugin = store => {
    store.subscribe((mutations, state) => {
        LocalForage.setItems(state)
    })
}

Merge states

Initial states are a best practice in Vue, and is required in this setup for stability (otherwise adding new state will potentially break previous local saves). It will then be merged with the saved state during store initialization, with priority given to the saved state.

// Initial app state
var initialState = {
    text: 'Hello world'
}

// Merge states and initialize store
async function initializeApp () {
    const savedState = await LocalForage.getItems()
    const mergedStates = _.merge({}, initialState, savedState)
    const store = new Vuex.Store({
        plugins: [persistPlugin],
        state: mergedStates,
        mutations: {},
        getters: {},
        actions: {}
    })
}

Full JS example

The following is a sample solution. Please note that our code requires es6 transpiler.

import Vuex from 'vuex'
import Vue from 'vue'
import LocalForage from 'localforage'
import 'localforage-getitems'
import 'localforage-setitems'
var _ = require('lodash')

// Configure localforage
LocalForage.config({
    driver      : LocalForage.IndexedDB,
    name        : 'APPNAME',
    version     : 1.0,
    storeName   : 'your-app-store-name'
});

// Our store plugin
const persistPlugin = store => {
    store.subscribe((mutations, state) => {
        LocalForage.setItems(state)
    })
}

// Initial app state
var initialState = {
    'text': 'Hello world'
}

// Merge states and initialize store
async function initializeApp () {
    const savedState = await LocalForage.getItems()
    const mergedStates = _.merge({}, initialState, savedState)
    const store = new Vuex.Store({
        plugins: [persistPlugin],
        state: mergedStates,
        mutations: {},
        getters: {},
        actions: {}
    })

    var app = new Vue({
        store,
        el: '#app',
        render: h => h(App)
    })
}

initializeApp()