Resetting redux state on logout
Solution 1
I wanted to point out to a couple of things that are pretty important before I give you a solution:
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 thatsubscribe
callback is incorrect and pointless.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
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, 2022Comments
-
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 ascreateRootReducer
: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 thehistory
object to it? -
Moshe Shmukler over 5 yearswhile your solution does not work, as is, even after we add the missing
)
aroundresetEnhancer
, 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 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 theresetEnhancer
I was doingaction !== 'RESET'
instead ofaction.type !== 'RESET'
. Try changing those 2 and I'm pretty sure that it will work :) -
Josep over 5 yearsUnfortunately what you are suggesting will also reinitialise the
router
property and I think that the OP is looking for a solution whererouter
property is not updated. Apart from that, your solution is quite similar to mine :) -
Moshe Shmukler over 5 yearsgot 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 about 5 yearswhat is the
createRootReducer(history)
and where it came from? -
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 over 2 yearshow to create a action type of name 'RESET' in redux toolkit , please suggest some ideas @Josep or anyone