Why not have state storage automatically integrated with Vuex? The way I was thinking of specifying it was like this:
// cart.js
export default {
state: {
items: [],
status: 'ok'
},
mutations: { ... },
save: ['items', 'status'] || { all: true }
}
Overriding the default storage provider:
// save-override.js
export default {
load(module, property) { // invoked on page load
return JSON.parse(localStorage.getItem(`${module}.${property}`))
},
save(state, module, property) {
localStorage.saveItem(`${module}.${property}`, state[module][property])
}
}
Saving through a middleware feels clunky somehow, like it doesn't fit with this specific purpose. A default storage implementation can be provided so only 1-liners are needed to enable saving/loading.
I think this is something that should be solved by the middleware, and I do have plans to improve the way middlewares work right now. Would you explain why you find it clunky?
This is the best I could come up with. I had to rename all of my mutations so that they start with the module name.
// middleware.js
function saveModule(modName, modState) {
for (let prop in modState) {
localStorage.setItem(`${modName}.${prop}`, JSON.stringify(modState[prop]))
}
}
export default {
onMutation (mutation, state, store) {
if (mutation.type.startsWith('CART_')) {
saveModule('cart', state.cart)
} else if (mutation.type.startsWith('COUNTRIES_')) {
saveModule('countries', state.countries)
} // ... on and on
}
}
The other way I was doing it was like this, which was terribly verbose:
// middleware.js
import * as Mutation from './mutations'
export default {
onMutation (mutation, state, store) {
switch(mutation.type) {
case Mutation.CART_ADD_ITEM:
case Mutation.CART_REMOVE_ITEM:
localStorage.setItem('cart.items', JSON.stringify(state.cart.items))
break
case Mutation.CART_ERROR:
case Mutation.CART_OK:
case Mutation.CART_PROCESSING:
localStorage.setItem('cart.status', JSON.stringify(state.cart.status))
break
// ... on and on
}
}
}
And nevermind loading the state with a bunch of:
// module.js
export default {
state: {
items: JSON.parse(localStorage.getItem('cart.items')) || [],
status: JSON.parse(localStorage.getItem('cart.status')) || 'ok',
foo: JSON.parse(localStorage.getItem('cart.foo')) || 0,
bar: JSON.parse(localStorage.getItem('cart.bar')) || 'zoo'
},
// ...
}
Don't get me started with onInit in the middleware, I tried that with PouchDB. More mutations with the exact same sort of code.
This is just something that I find can be relatively easily implemented into Vuex. It gets storage up and running with minimal effort, reduces the lines of code, and reduces the time to refactor if necessary.
(assuming 2.0)
I think this can be done by adding a field in the mutation's payload:
store.commit('SOME_MUTATION', {
save: 'cart'
})
In plugin:
store.subscribe((mutation, state) => {
const moduleToSave = mutation.payload.save
if (moduleToSave) {
saveModule(moduleToSave, state[moduleToSave])
}
})
@piknik I've created a module for exactly that :) https://github.com/robinvdvleuten/vuex-persistedstate
I had the same problem and I wanted to
1) save the state to the local storage on every mutation
2) set store state on first load, before anything else executed.
I ended up writing a small vuex plugin for this. The mutations are hardcoded, but you can get the idea:
import {saveToLocal, getFromLocal} from '../../utils/localStore';
const persistUser = (store) => {
const agreed = getFromLocal('agreed');
const likes = getFromLocal('likes');
store.commit('user/AUTHORIZE', agreed);
store.commit('user/SET_LIKES', likes);
store.subscribe((mutation, state) => {
const type = mutation.type;
if (type === 'user/LIKE' || type === 'user/UNLIKE') {
saveToLocal('likes', state.user.likes);
}
if (type === 'user/ALLOW_TRACKING') {
saveToLocal('track', mutation.payload);
}
if (type === 'user/ALLOW_PUSH') {
saveToLocal('allowPush', mutation.payload);
}
})
};
export default persistUser;
I hope it can help somebody else... Also note that localStorage does not work as expected in incognito mode and can break your app.
Most helpful comment
(assuming 2.0)
I think this can be done by adding a field in the mutation's payload:
In plugin: