Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resetting redux state on logout

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?

like image 323
Moshe Shmukler Avatar asked Jan 15 '19 13:01

Moshe Shmukler


People also ask

How do you reset state in Redux?

Redux-persist keeps a copy of your state in a storage engine, and the state copy will be loaded from there on refresh. First, you need to import the appropriate storage engine and then, to parse the state before setting it to undefined and clean each storage state key.

Does Redux state reset on refresh?

All data in redux store will be cleared to initial state when client refresh our application on the browser or close the browser's tab. So if our application have about user permission, role or something that for protected data.

How do I reset my state in React?

To reset a component to its initial state:Store the initial state in a variable. When an event occurs, call the setState() function, passing it the initial state.

Can we change Redux state directly?

Redux restricts updating the state to this method only. This strict way of updating the state ensures that the state can not be changed directly either by view or any network callback. The only way to update a state is by defining the action and then dispatching it. Remember that actions are plain JavaScript objects.

How do I reset an app in Redux?

The trick to reuse the logic for resetting the app in a single place is to create a root reducer over your app root reducer. That is a reducer on top of your reducers where you check for that condition and apply it if necessary. Usually, you would use the combineReducers function to create a single root reducer for your redux store:

How to reset the state of a redux store in ReactJS?

Steps to reset the state of a redux store. 1. Implement redux store in ReactJS. In order to show you the example, you have to implement redux store in ReactJS. If you don’t know how to ... 2. Update store to reset state. 3. Update component to reset the store. 4. Output.

Should I refresh the browser after adding a new redux state?

When you do a new redux state, with empty content, you basically still have the previous states in memory, and you could theoretically access the data from them. Refreshing the browser IS your safest bet! With Redux if have applied the following solution, which assumes I have set an initialState in all my reducers (e.g. { user: { name, email }} ).

How to reset the default state of the store using reducer?

This way whenever you call RESET action, you reducer will update the store with default state. const logoutHandler = () => { store.dispatch (RESET_ACTION) // Also the custom logic like for the rest of the logout handler } Every time a userlogs in, without a browser refresh. Store will always be at default.


2 Answers

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.

like image 199
Josep Avatar answered Nov 15 '22 15:11

Josep


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

like image 30
Jose A. Ayllón Avatar answered Nov 15 '22 16:11

Jose A. Ayllón