When is the plugin ready to be used?
PierBover opened this issue ยท 13 comments
Loading from localStorage
seems immediate when the application starts, but when writing to the vuex store persistence doesn't seem to work until some time has passed.
If I do this in root component the state in localStorage
doesn't change:
const root = new Vue({
el: '#app',
router,
store,
render: (h) => h(App),
mounted () {
store.commit('persistent/SET_TEST_VAR', true);
console.log(store.state.persistent.test_var);
}
});
If I do this instead it works fine:
setTimeout(() => {
store.commit('persistent/SET_TEST_VAR', true);
console.log(store.state.persistent.test_var);
}, 1000);
I managed to find a workaround if you're using VueRouter
Given you have the strict RESTORE_MUTATION
setup;
// ./store/index.js
import VuexPersistPatch from './plugins/vuex-persist-patch'
export default new Vuex.Store({
mutations: Object.assign(mutations, {
RESTORE_MUTATION: vuexLocal.RESTORE_MUTATION
}),
plugins: [
vuesLocal.plugin,
VuexPersistPatch()
]
})
// ./plugins/vuex-persist-patch.js
export default function vuexPersistPatch () {
return store => {
store._vm.$root.$data['vue-persist-patch-delay'] = true
store.subscribe(mutation => {
if (mutation.type === 'RESTORE_MUTATION') {
store._vm.$root.$data['vue-persist-patch-delay'] = false
store._vm.$root.$emit('storageReady')
}
})
}
}
Take special note to using store._vm
to receive the event from the bus.
// ./router/index.js
import store from '../store'
router.beforeEach((to, from, next) => {
if (store._vm.$root.$data['vue-persist-patch-delay']) {
// Hold the request, until the Storage is complete.
store._vm.$root.$on('storageReady', () => {
next({path: to.path, query: to.query, params: to.params})
})
} else {
// YOUR OTHER RULES GO HERE
}
})
Thanks @kylewelsby for the suggestion.
Seems overly complicated though since localStorage
interactions are not async.
I ended up using vuex-persistedstate which works as expected.
The problem has been resolved in v1.0
If the store is not async (you're using localStorage), then vuex-persist will synchrnously restore state and you won't feel like "The store is not immediately restored"
@championswimmer I've just tested out the changes in version 1.0 and it seems as though your fix for this behavior does not apply when using sessionStorage() instead of localStorage().
Would it be possible to make the two types of storage behave the same, sync way, or is there something that would prevent someone from doing that? It'd really helpe if sessionStorage() could also work this way.
Thanks for your work on this project :)
Thank you @PierBover. I managed to do this with localforage inspired by your plugin.
//first define a plugin that emits when the state has been persisted
const vuexPersistEmitter = () => {
return store => {
store.subscribe(mutation => {
if (mutation.type === 'RESTORE_MUTATION') {
store._vm.$root.$emit('storageReady');
}
});
}
};
//I only needed to define strict mode here and not in the store.
const vuexLocal = new VuexPersistence({
strictMode: true,
asyncStorage: true,
storage: localforage
});
export default new Vuex.Store({
...,
mutations: {
RESTORE_MUTATION: vuexLocal.RESTORE_MUTATION
},
plugins: [
vuexLocal.plugin,
vuexPersistEmitter()
]
});
And then in any kind of component:
const app = new Vue({
store,
...App,
mounted() {
const self = this;
self.$store._vm.$root.$on('storageReady', () => {
self.$store.dispatch('somethingThatDependsOnTheStateBeingFilled');
});
}
});
Hi, thanks for this plugin ๐ using custom methods for the storage (setItem, getItem, etc) instead of localforage will fail unless the flag here is set too
Tried this plugin with 1.0.0 and "The store is not immediately restored". Is there a fix of this in any of the new versions or do I implement a workaround?
Tries this plugin with 1.1.5 and still the same issue. using @CosX workaround does help but only if the state is restored....so I have to check the regular way and via store events
created(){
this.setFetching({fetching: true})
this.$store._vm.$root.$on('storageReady', () => {
this.setGameStates()
this.setFetching({fetching: false})
});
},
mounted() {
this.setGameStates()
}
Just a note if you are having this timing issue on production mode and is trying to use the subscribe trick.
As you will have strict
set to false, the subscribe
with RESTORE_MUTATION
won't work.
That's because the store won't commit, but rather replace the state.
Look the code here:
Lines 163 to 167 in a5033b5
@championswimmer Can we have some event emitted here to know when that has happened on production mode?
EDIT:
Based on #3 (comment),
I have made this to be notified when storage has been replaced:
import store from '@/store' //<-- your store module
export default new VuexPersistence({
storage: localForage,
strictMode: false,
asyncStorage: true,
restoreState: (key, storage) => {
return new Promise(resolve => {
storage
.getItem(key)
.then(data => {
resolve(data);
store._vm.$f7.emit('storageReady');
})
})
},
...
});
I managed to find a workaround if you're using VueRouter
Given you have the strict
RESTORE_MUTATION
setup;// ./store/index.js import VuexPersistPatch from './plugins/vuex-persist-patch' export default new Vuex.Store({ mutations: Object.assign(mutations, { RESTORE_MUTATION: vuexLocal.RESTORE_MUTATION }), plugins: [ vuesLocal.plugin, VuexPersistPatch() ] })// ./plugins/vuex-persist-patch.js export default function vuexPersistPatch () { return store => { store._vm.$root.$data['vue-persist-patch-delay'] = true store.subscribe(mutation => { if (mutation.type === 'RESTORE_MUTATION') { store._vm.$root.$data['vue-persist-patch-delay'] = false store._vm.$root.$emit('storageReady') } }) } }Take special note to using
store._vm
to receive the event from the bus.// ./router/index.js import store from '../store' router.beforeEach((to, from, next) => { if (store._vm.$root.$data['vue-persist-patch-delay']) { // Hold the request, until the Storage is complete. store._vm.$root.$on('storageReady', () => { next({path: to.path, query: to.query, params: to.params}) }) } else { // YOUR OTHER RULES GO HERE } })
@kylewelsby Thank you !! this served me!