Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

asyncStorage: Commit not persisted when actions are chained #56

Closed
morphatic opened this issue Jun 23, 2018 · 4 comments
Closed

asyncStorage: Commit not persisted when actions are chained #56

morphatic opened this issue Jun 23, 2018 · 4 comments

Comments

@morphatic
Copy link

I'm using localForage as my storage as well as Vuex modules. My root store looks like this:

import Vue from "vue";
import Vuex from "vuex";
import localforage from "localforage";
import VuexPersistence from "vuex-persist";
import auth from "./modules/auth";

Vue.use(Vuex);

const local = new VuexPersistence({ storage: localforage });

export default new Vuex.Store({
  modules: { auth },
  mutations: { RESTORE_MUTATION: local.RESTORE_MUTATION },
  plugins: [local.plugin]
});

In my auth module, I've got two actions. The first one logs the user in, and the second one retrieves the user's profile if the login is successful:

const actions = {
  login({ commit, dispatch }) {
    return login().then(
      res => {
        commit(LOGIN_SUCCESS, res); // <-- THIS COMMIT IS CALLED BUT NOT PERSISTED
        return dispatch("getProfile", res.user_id);
      },
      err => commit(LOGIN_FAILURE, err)
    );
  },
  getProfile({ commit }, user_id) {
    commit(PROFILE_REQUEST);
    return api.getProfile(user_id).then(
      res => commit(PROFILE_SUCCESS, res),
      err => commit(PROFILE_FAILURE, err)
    );
  }
};

The problem is that the data in the LOGIN_SUCCESS commit does not get persisted to the store. I know that the login is successful, because I stuck a console.log() in the LOGIN_SUCCESS mutation to make sure it was being called. I also know that it is successful because the getProfile(user_id) API call successfully retrieves the user's profile and stores it in the state when commit(PROFILE_SUCCESS, res) is called. The profile gets persisted, but not the login information.

It seems to work fine in production mode but NOT in development mode. I tried turning strictMode on and off (in both the Vuex store instance and in the VuexPersistence object), but that didn't seem to make a difference.

Oddly, if I put a console.log("hi!") statement at the beginning of the login() action, it seems to work fine in development mode. This made me think the problem may be related to #15, i.e. the RESTORE_MUTATION fires after LOGIN_SUCCESS and therefore overwrites it. That being said, devtools doesn't show that the LOGIN_SUCCESS mutation ever happened. I did try writing a plugin like the one @CosX described here:

const storageReady = () => {
  return store => {
    store.subscribe(mutation => {
      if ("RESTORE_MUTATION" === mutation.type) {
        store._vm.$root.$emit("storageReady");
      }
    });
  };
};

But I couldn't figure out how to get a reference to the $store and therefore the $on() event listener from within a store module.

I clearly don't want to keep a random console.log() statement in my login() action forever. That being said, if I can't use VuexPersistence in development mode, it will make this app very tedious to develop. I would switch to just use localStorage, but I expect this app will have a fair amount of data associated with it and I'd like for people to be able to use it offline as a PWA, so this and this suggest I'm better off sticking with localForage because it uses IndexedDB.

Any suggestions? Thanks!!!

@morphatic
Copy link
Author

I found another (not very satisfactory) workaround that seems to confirm that the problem has something to do with the asynchronous nature of localForage. I revised my actions to look like this:

const sleep = ms => new Promise(res => setTimeout(res, ms));

const actions = {
  async login({ commit, dispatch }) {
    await sleep(500); // <-- FORCE A 500ms DELAY
    return login().then(
      res => {
        commit(LOGIN_SUCCESS, res); // <-- THIS COMMIT GETS PERSISTED NOW!
        return dispatch("getProfile", res.user_id);
      },
      err => commit(LOGIN_FAILURE, err)
    );
  },
  getProfile({ commit }, user_id) {
    commit(PROFILE_REQUEST);
    return api.getProfile(user_id).then(
      res => commit(PROFILE_SUCCESS, res),
      err => commit(PROFILE_FAILURE, err)
    );
  }
};

This workaround will allow me to continue working in development mode, but it's something I'll have to remember to remove before doing a production build. Also, it makes me worry that I don't fully understand the async issue and could have a bug in my production code...

@rulrok
Copy link

rulrok commented Sep 24, 2018

@morphatic See my new comment on #15 to see if it applies to you.

@championswimmer
Copy link
Owner

please reopen if still issue remains

@morphatic
Copy link
Author

It turns out this issue was a result of state being asynchronously restored after mutations that were committed on page load. #107 introduces an event that gets emitted when the async store update has completed. Now that I understand the timing issues better, it all works as intended. Thanks for a great plugin!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants