Resetting redux state on logout

16,720

Solution 1

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.

Solution 2

You can make an action for clearing the store and use it in your rootReducer as like this:


const appReducer = combineReducers({
    ... // your reducers
});

const rootReducer = (state, action) => {
    if (action.type === 'CLEAR_STORE') return appReducer(undefined, action);
    return appReducer(state, action);
};

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export const configureStore = (initialState, history) =>
    createStore(
        connectRouter(history)(rootReducer),
        initialState,
        composeEnhancers(applyMiddleware(routerMiddleware(history), thunkMiddleware))
    );

You'll have appReducer with all your reducers but also rootReducer which will be the function that will determinate returning a corresponding value or an undefined one.

TIP: You can use your Logout action rather than using a new action just for clearing the store

Share:
16,720
Moshe Shmukler
Author by

Moshe Shmukler

I am a former low-level-software developer turned Enterprise Software startup executive. Now, I am running a two person mini- startup/incubator doing programming, and sometimes helping with marketing and UX. Occasionally, I take-on consulting gigs coding proprietary and open-source software. I occasionally contribute to some high-profile OSS projects such as Mono, and maintain Novell.Directory.LDAP library.

Updated on June 08, 2022

Comments

  • Moshe Shmukler
    Moshe Shmukler almost 2 years

    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?

  • Moshe Shmukler
    Moshe Shmukler over 5 years
    while your solution does not work, as is, even after we add the missing ) around resetEnhancer, I do think that your thinking is correct. Thank you. Definitely looks like the correct general direction. The state does not reset, though. ;( I will keep playing with what you gave me. Appreciate the help.
  • Josep
    Josep over 5 years
    @MosheShmukler I just updated my answer... There were 2 things that were wrong: the closing parenthesis around resetEnhancer and the fact that inside the resetEnhancer I was doing action !== 'RESET' instead of action.type !== 'RESET'. Try changing those 2 and I'm pretty sure that it will work :)
  • Josep
    Josep over 5 years
    Unfortunately what you are suggesting will also reinitialise the router property and I think that the OP is looking for a solution where router property is not updated. Apart from that, your solution is quite similar to mine :)
  • Moshe Shmukler
    Moshe Shmukler over 5 years
    got it working. Your version was 99% correct, and the idea was totally right.``` const resetEnhancer = rootReducer => (state, action) => { if (action.type !== 'LOGOUT_USER') { return rootReducer(state, action); } const newState = rootReducer(undefined, { type: 'LOGOUT_USER' }); newState.router = state.router; return newState; };```
  • The Coder
    The Coder about 5 years
    what is the createRootReducer(history) and where it came from?
  • Josep
    Josep about 5 years
    @TheCoder it is explained in the OPs question. It is the first snipped of code that appears in the question. Also, just after that snipped the OP says: "Then I do configureStore, importing the above as createRootReducer"
  • Aashiq
    Aashiq over 2 years
    how to create a action type of name 'RESET' in redux toolkit , please suggest some ideas @Josep or anyone