championswimmer / vuex-persist

A Vuex plugin to persist the store. (Fully Typescript enabled)
http://championswimmer.in/vuex-persist
MIT License
1.67k stars 117 forks source link

store.restored Promise resolves before the state is restored. #161

Open wujekbogdan opened 4 years ago

wujekbogdan commented 4 years ago

Here's the restoreState function that retrieves some data from Firebase based on localStorage wishlistId

restoreState: async (key, storage) => {
  const snapshot = await db
    .ref(`wishlist/${storage.wishlistId}`)
    .once('value');

  const data = snapshot.val();

  console.log('restoring', data);

  return {
    wishlist: {
      ids: data.ids || [],
    },
  };
},

Here's the beforeEnter route callback.

beforeEnter: async (to, { params }, next) => {
  console.log('before restore');
  await store.restored;
  console.log('after restore');

  // ... sone additional logic

  next();
},

I would expect the messages to be logged in the following order:

But the messages are logged in this order:


As a result, the router redirects to a route before the state is restored. I have asyncStorage set to true

wujekbogdan commented 4 years ago

I found why it happens. I forgot I have multiple instances of VuexPersistence, so the store.restored Promise resolves only once.

Is there any way to get a promise per-instance?

nwittwer commented 4 years ago

I think I'm having a similar issue in a Nuxt universal mode app. However for me, await store.restored is returning undefined always.

plugins/router.js

export default ({ store, app }) => {
  app.router.beforeEach(async (to, from, next) => {
    await store.restored // <-- returns "undefined"
    next()
  })
}

plugins/vuex-persist.js

// ~/plugins/vuex-persist.js
import VuexPersistence from 'vuex-persist'

export default ({ store }) => {
  window.onNuxtReady(() => {
    new VuexPersistence({
      /* your options */
    }).plugin(store)
  })

Using:

softzer0 commented 4 years ago

Having the same problem here as @nwittwer, but without Nuxt - store.restored is always undefined. Is there some fix or workaround for this?

paichinger commented 3 years ago

I had the same problem. It started working with the following setup (in nuxt):

vuex-persist.js

import VuexPersistence from 'vuex-persist'
import localforage from 'localforage'

export default ({ store }) => {
      new VuexPersistence({
        key: 'your-key',
        storage: localforage,
        asyncStorage: true, // THIS SEEMS TO BE IMPORTANT FOR localforage, with window.localstorage it also worked without it
    }).plugin(store);
}

waitForStore.js - this is basically the adaption of the vuex-persist authors recommendation for nuxt

export default function ({store, app}) {
    const waitForStorageToBeReady = async (to, from, next) => {
        await store.restored
        next()
    }
    app.router.beforeEach(waitForStorageToBeReady);
}

Add both plugins to nuxt.config.js

plugins: [
    { src: '~/plugins/vuex-persist', ssr: false }, '~/plugins/waitForStore'
  ]

Hope it helps...

dsschneidermann commented 3 years ago

I don't believe there is a bug here, but the issue highlights a need for documenting the correct setup.

If your vuex persist installation happens in window.onNuxtReady, then you cannot have the global router work before onNuxtReady has also been called. The store.restored property won't be there because it is only added later.

I have tried adding the window.onNuxtReady to the router plugin, but that also doesn't work - it makes the router change not apply. So the fix is instead to remove window.onNuxtReady from the installation.

  window.onNuxtReady(() => { // .. Remove this ..
    new VuexPersistence({