Resetting redux state on logout

Moshe Shmukler picture Moshe Shmukler · Jan 15, 2019 · Viewed 12.6k times · Source

I am trying to deal with the problem, addressed, in general in an earlier thread - How to reset the state of a Redux store? - the need to reinitialize/invalidate the entire redux store on user logout.

In my case, however, something is still missing. I am using Redux with ConnectedRouter and I tried to do the following.

Define the rootReducer as:

export default history => 
  combineReducers({
    router: connectRouter(history),
    user,
    manager,
    vendor
    // rest of your reducers
  });

Then I do configureStore, importing the above as createRootReducer:

const configureStore = (initialState = {}, history) => {
  let composeEnhancers = compose;
  const composeWithDevToolsExtension =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
  const enhancers = [];
  const middleware = [sagaMiddleware, thunk, routerMiddleware(history)];
  if (typeof composeWithDevToolsExtension === 'function') {
    composeEnhancers = composeWithDevToolsExtension;
  }
  const store = createStore(
    createRootReducer(history), // root reducer with router state
    initialState,
    composeEnhancers(applyMiddleware(...middleware), ...enhancers)
  );
  store.runSaga = sagaMiddleware.run;
  store.subscribe(() => {
    const state = store.getState();
    const { session } = state['user'];
    if (!session) {
      console.log('no valid session');
      initialState = undefined;
    } else {
      const { token } = session;
      const decodedToken = jwt_decode(token);
      const { exp } = decodedToken;
      const now = new Date();
      if (exp > now.getTime()) {
        console.warn('token expired');
        initialState = undefined;
      } else {
        console.log('token valid');
      }
    }
  });
  return store;
};
export default configureStore({}, history);

The idea is that initialState = undefined; should reset my state. It is not working for me though.

Where would be the correct place to do this, given that I am using the ConnectedRouter and passing the history object to it?

Answer

Josep picture Josep · Jan 15, 2019

I wanted to point out to a couple of things that are pretty important before I give you a solution:

  1. When using redux you should never attempt to alter the state of your store directly. The state should always change through the reducer as a reaction to an action. Therefore, reassigning the parameter initialState inside that subscribe callback is incorrect and pointless.

  2. I don't think that you want to "reset" the state of the router property, correct?

One way to solve this is to use a reducer enhancer, something like this:

const resetEnhancer = rootReducer => (state, action) => {
  if (action.type !== 'RESET') return rootReducer(state, action);

  const newState = rootReducer(undefined, {});
  newState.router = state.router;
  return newState;
};

And then when you create your store do this:

  const store = createStore(
    resetEnhancer(createRootReducer(history)),
    initialState,
    composeEnhancers(applyMiddleware(...middleware), ...enhancers)
  );

And in that subscribe callback do this:

if (!session) {
  store.dispatch({type: 'RESET'});
}

One last extra-tip: since you are using redux-saga I strongly suggest that you move what you are doing inside that subscribe callback into a saga.