ngrx-store-localstorage: rehydratedState is not recalculated on UPDATE_ACTION

rehydratedState is defined only on INIT_ACTION. When localStorage is updated and then lazy-module is loaded, UPDATE_ACTION called and old state (rehydratedState from INIT) is used.

if ((action.type === INIT_ACTION || action.type === UPDATE_ACTION) && rehydratedState) { nextState = merge({}, nextState, rehydratedState); }

Use case:

  1. Login => token is saved in localstorage
  2. Reload the page => rehydratedState created with token
  3. Login with different user => new token stored in localstorage (but not synced with rehydratedState!)
  4. Go to lazy loaded page => UPDATE_ACTION, token is overridden with old token from step 2 😦((

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 3
  • Comments: 19 (3 by maintainers)

Most upvoted comments

If you want to skip all UPDATE_ACTION actions and don’t want to wait for this PR to be merged, there’s a simple way to do it. Replace your local storage sync reducer with a variant of this:

export function localStorageSyncReducer<T, V extends Action = Action> (reducer: ActionReducer<any>): ActionReducer<T, V> {
  
  return (state, action) => {
    if (action.type === '@ngrx/store/update-reducers')
      return reducer(state, action);

    return localStorageSync({
      rehydrate: true,
      removeOnUndefined: true,
      keys: []
    })(reducer)(state, action)
  };
}

@dimitriy-k Have you forked the project? I don’t see it in your GitHub profile. Make sure you’re not cloning this project but rather made a fork and cloned that.

We had the same use case causing the previous logged-in user to become active once the other user clicked on lazy-loaded route. Here’s our meta reducer to sync session to/from local storage (old version).

export function sessionSyncReducer(reducer: ActionReducer<any>) {
  return localStorageSync({
    keys: [
      {user: ['session']}
    ],
    rehydrate: true,
    removeOnUndefined: true,
    syncCondition: (state: IAuthStore) => state.rememberMe === true
  })(reducer);
}

This version works like expected:

export function sessionSyncReducer(reducer: ActionReducer<any>) {
  return (state, action) => {
    return localStorageSync({
      keys: [
        {user: ['session']}
      ],
      rehydrate: true,
      removeOnUndefined: true,
      syncCondition: (state: IAuthStore) => state.rememberMe === true
    })(reducer)(state, action);
  }
}

This issue seems pretty complicated, given all the different discussions going on.

Is someone able to provide a repro for this? You might be able to fork this stackblitz which was used to reproduce a different issue.

Well, I discovered today that due to the comments mentioned in the original report (INIT not being called for lazy loaded module metareducers), I needed to re-add the check for UPDATE. A better solution just seems to be to clear out the captured value of hydratedState once it’s used. That seems to fix the original issue, but also prevent any accidental re-applying of the restored hydrated state (which could live on, long after ngrx state has been cleared)

looks like UPDATE_ACTION is not needed at all

https://github.com/btroncone/ngrx-store-localstorage/pull/122