How to reset the state of a Redux store?
Asked Answered
E

36

640

I am using Redux for state management.
How do I reset the store to its initial state?

For example, let’s say I have two user accounts (u1 and u2).
Imagine the following sequence of events:

  1. User u1 logs into the app and does something, so we cache some data in the store.

  2. User u1 logs out.

  3. User u2 logs into the app without refreshing the browser.

At this point, the cached data will be associated with u1, and I would like to clean it up.

How can I reset the Redux store to its initial state when the first user logs out?

Everick answered 25/2, 2016 at 9:0 Comment(2)
It is probably better to clear the state on logout instead (from a security perspective)Hipped
For redux toolkit: How to reset state of Redux Store when using configureStore from @reduxjs/toolkit?Decidua
D
1357

One way to do that would be to write a root reducer in your application.

The root reducer would normally delegate handling the action to the reducer generated by combineReducers(). However, whenever it receives USER_LOGOUT action, it returns the initial state all over again.

For example, if your root reducer looked like this:

const rootReducer = combineReducers({
  /* your app’s top-level reducers */
})

You can rename it to appReducer and write a new rootReducer delegating to it:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
})

const rootReducer = (state, action) => {
  return appReducer(state, action)
}

Now we just need to teach the new rootReducer to return the initial state in response to the USER_LOGOUT action. As we know, reducers are supposed to return the initial state when they are called with undefined as the first argument, no matter the action. Let’s use this fact to conditionally strip the accumulated state as we pass it to appReducer:

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

  return appReducer(state, action)
}

Now, whenever USER_LOGOUT fires, all reducers will be initialized anew. They can also return something different than they did initially if they want to because they can check action.type as well.

To reiterate, the full new code looks like this:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
})

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

  return appReducer(state, action)
}

In case you are using redux-persist, you may also need to clean your storage. 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.

const rootReducer = (state, action) => {
    if (action.type === SIGNOUT_REQUEST) {
        // for all keys defined in your persistConfig(s)
        storage.removeItem('persist:root')
        // storage.removeItem('persist:otherKey')

        return appReducer(undefined, action);
    }
    return appReducer(state, action);
};
Delanos answered 26/2, 2016 at 1:54 Comment(25)
I'm curious Dan, could you also do something like this in your reducer. with CLEAR_DATA being the action. case 'CLEAR_DATA': return initialStateAbbotsun
@Abbotsun that would work but not on the state for every reducer.Licko
Here is a version where you dynamically combine the reducers in case you use async reducers: export const createRootReducer = asyncReducers => { const appReducer = combineReducers({ myReducer ...asyncReducers }); return (state, action) => { if (action.type === 'LOGOUT_USER') { state = undefined; } return appReducer(state, action); } };Archducal
What if that initial state also includes some external data, i.e. from localStorage?Silverts
if (action.type === 'RESET') return action.stateFromLocalStorageDelanos
Does this approach completely clear the state and all of its history? I'm thinking from a security perspective: If this has been implemented, once the USER_LOGOUT action has been fired, is it possible to obtain state data from earlier on? (e.g. via devtools)Dulci
I have a question myself. So I logged when the action and reducers gets called. I also logged when my mapStateToProps gets called. I noticed that after the clearing event, my mapStateToProps fires with the previous states...I thought this was suppose to fix thatBelanger
Hey is this solution working for you guys? In my case its clearing the states on the logger but on the UI I get to see the same previous components (e.g, if a form was prefilled and after doing logout and login again I get to see the input fields filled up with the previous data)Tameratamerlane
What if I need to update the store with new values from the server for a different user.? The above method will only reset the state. The only way is to recreate the store ?. What happens to the components subscribed to the previous store.Braddock
How would one go about registering a RESET reducer like we see in this answer with undo logic or redux-undo?Stringfellow
@ivo-sabev Is there any way to unit test the code you have mentioned using jasmine?Beep
@DanAbramov Thank you for this example! But I wonder if this approach secured enough to be sure that user's data isn't available after logging out? I curious don't we need to do location.reload() anyway to clean network requests for example?Campbellbannerman
@DanAbramov passing undefined as the first argument to reducer doesn't seem possible when using typescript typings Reducer<S> = (state: S, action: AnyAction) => SHildredhildreth
Rocking solution to clear whole redux state and fallback to default state values of each reducer.Smaragdite
@Dulci Redux store doesn't store history itself so it's completely safe. And to use redux dev tools you must include them in your build, what you definitely shouldn't do for production build. Other than that to access redux store user's browser must be hijacked in some way, in which case you have no means to control what data may leak.Ljoka
undefined causes more trouble than it's worth, especially if you're hooked into immutable-js. with undefined at the root, all of your gets and getIns fail and the entire application falls apart.Joab
@DanAbramov Do I need to add rootReducer to appReducer? Sorry, I am very new to UI so not able to figure it out. I am not sure how to add rootReducer to the storeLakesha
@DanAbramov redux-persist is now saving everything under the key 'persist:root'. Check my editObviate
One note on this answer. If you are using redux persist, removing _persist key will break redux-persist function, so for persistent state objects, you want to return { _persist: state._persist } or else you will break persist function. You can test this out by logging out then logging in again, if u hit refresh you will see that data wasnt saved in redux-persist and you got logged out, but if you hit refresh before logging in again, all will be fine as _persist key would get regenerated.Margarettamargarette
Good solution but cause a type error if using Typescript, someone has a clean workaround or type correction instead of ignoring it with // @ts-ignore ?Medea
When implementing this solution, I find that for an item from local storage used to initialize a reducer item, while always cleared in local storage on sign-out, it sometimes persists in state after sign-out. It appears that when resetting to initial state, redux sometimes recognizes an initial state setting like 'token: localStorage.getItem('token') as equal to initial state, and so it doesn't reset it to the latest value for token on sign-out (i.e. null). Has anyone had similar experience and found a reliable solution?Pavo
If you're using redux-persists, after setting state to undefined yo need to "reinit" persist store. Because persist store metadata is lost, so redux persist will not persist any store changes from that moment. What I do instead is calling persistor.flush and persistor.persist to reinit persist store.Beebeebe
is this solution working for you guys? I have deeply nested object in the reducer and some values are not cleared even if I set the rootReducer(undefined, action) and confirmed that it went in there. Am I missing something? Is it possible that it is being overwritten again by other default case from other reducers?Cranny
storage.removeItem('persist:root') only worked despite trying all solutionsDavena
Is there any way to keep one slice's state in this approach?Grip
A
97

Dan Abramov's answer is correct except we experienced a strange issue when using the react-router-redux package along with this approach.

Our fix was to not set the state to undefined but rather still use the current routing reducer. So I would suggest implementing the solution below if you are using this package

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    const { routing } = state
    state = { routing } 
  }
  return appReducer(state, action)
}
Apgar answered 20/5, 2016 at 5:27 Comment(5)
I think the takeaway here is that you may not want to clear the whole state tree on logout - the approach works equally well at the root reducer of any subtree, and so it may be clearer to apply this technique only at the root reducers of the subtree(s) you do want to clear, rather than picking out 'special' children to not clear at the root reducer of the entire tree, like thisScanties
I think i am experiencing this issues you are referring to right now, (where on logout it will set the rout to the right path but a complete different component would load) i implemented something similar to yours to fix it, but i think something with immutable js is massing this up. I ended up creating a parent reducer that has RESET-STATE action, and i inherit from that reducer to avoid touching routing altogetherElwina
Was experiencing similar issues, this has fixed it. Thanks.Contradistinguish
Note that with react-redux-router, the property is router and NOT rountingFerdelance
@Ferdelance it depends what you defined it as in your combineReducers() ..... if you had combineReducers({routing: routingReducer}) it would be as described in the answerVehicle
S
79

Define an action:

const RESET_ACTION = {
  type: "RESET"
}

Then in each of your reducers assuming you are using switch or if-else for handling multiple actions through each reducer. I am going to take the case for a switch.

const INITIAL_STATE = {
  loggedIn: true
}

const randomReducer = (state=INITIAL_STATE, action) {
  switch(action.type) {
    case 'SOME_ACTION_TYPE':

       //do something with it

    case "RESET":

      return INITIAL_STATE; //Always return the initial state

   default: 
      return state; 
  }
}

This way whenever you call RESET action, you reducer will update the store with default state.

Now, for logout you can handle the like below:

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.

store.dispatch(RESET_ACTION) just elaborates the idea. You will most likely have an action creator for the purpose. A much better way will be that you have a LOGOUT_ACTION.

Once you dispatch this LOGOUT_ACTION. A custom middleware can then intercept this action, either with Redux-Saga or Redux-Thunk. Both ways however, you can dispatch another action 'RESET'. This way store logout and reset will happen synchronously and your store will ready for another user login.

Subterfuge answered 27/12, 2017 at 19:17 Comment(11)
i feel like this is the better approach than just setting state to undefined like in the other answer. when your application is expecting a state tree and you give it undefined instead, there's just more bugs and headaches to deal with than just an empty tree.Joab
@Joab The state won't actually be undefined, because reducers return initialState when they receive an undefined stateForeland
@Joab think that with this approach, each time anyone create a new reducer you will have to remember to add the reset case.Wasteland
i've definitely changed my mind on this, for both those reasons, plus the idea that a RESET_ACTION is an action. so it doesn't really belong in the reducer to begin with.Joab
This is definitely the right approach. Setting the state to anything but the Initial State is just asking for troubleKimberlite
This isn't an efficient solution at all. You have to add the RESET case to each of your reducers rather than just once in your root reducer. Imagine a new developer who doesn't know about this when he/she creates a new reducer. I'm definitely going with Dan's approach.Koblas
This is definitely the right approach! The little extra work of adding a RESET case to all reducers pays of in terms of being able to control every part of your store. Some parts of the store you might not wish to clear, other parts you might wish to reset to default state, and some you want to reset. Many thanks nirbhaygp for a elegant solution!Jenellejenesia
I'm always use this approach. For all my cases I want to store some user related not secured data between users comebacks. For example it might be something like lastViewedPage or clicksCount. Anyway there is a more less work than store it in persist storage.Dykes
@Jenellejenesia over the period since I have anwsered, I now use a base reducer composer which gives me the all such actions handled.Subterfuge
can you help me with your idea ?Penates
This is the best approach because it prevents the user NOT from exiting the session and always resets the state, also in case he exits the session the right way it also resets the state, this approach is very useful when applying REDUX- SAGAFaith
G
33

Just a simplified answer to Dan Abramov's answer:

const rootReducer = combineReducers({
    auth: authReducer,
    ...formReducers,
    routing
});


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);
Geognosy answered 13/8, 2018 at 21:54 Comment(2)
This is the best solution to it.Quenby
Where do you put this code?Starr
S
32

Using Redux Toolkit and/or Typescript:

const appReducer = combineReducers({
  /* your app’s top-level reducers */
});

const rootReducer = (
  state: ReturnType<typeof appReducer>,
  action: AnyAction
) => {
/* if you are using RTK, you can import your action and use it's type property instead of the literal definition of the action  */
  if (action.type === logout.type) {
    return appReducer(undefined, { type: undefined });
  }

  return appReducer(state, action);
};
Shrug answered 21/9, 2020 at 12:45 Comment(2)
This solution worked for me with small change. I needed to add undefined as optional type for the state, so it can be used with configureStore "state: ReturnType<typeof appReducer> | undefined"Infundibulum
A more complete answer for redux toolkit is here: https://mcmap.net/q/65118/-reset-state-to-initial-with-redux-toolkitStarr
E
20

From a security perspective, the safest thing to do when logging a user out is to reset all persistent state (e.x. cookies, localStorage, IndexedDB, Web SQL, etc) and do a hard refresh of the page using window.location.reload(). It's possible a sloppy developer accidentally or intentionally stored some sensitive data on window, in the DOM, etc. Blowing away all persistent state and refreshing the browser is the only way to guarantee no information from the previous user is leaked to the next user.

(Of course, as a user on a shared computer you should use "private browsing" mode, close the browser window yourself, use the "clear browsing data" function, etc, but as a developer we can't expect everyone to always be that diligent)

Egotism answered 9/5, 2018 at 23:41 Comment(3)
Why have people downvoted this? 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!Smail
thanks. this is what i was looking at, all other answers are merely returning a new state rather than reset it as asked by OPFerryboat
This is hands down the best answer. I spent a whole day trying all things mentioned every place and problems just kept popping up left, right, and centre. I kept wondering if I should just reload the whole thing instead of wasting time adding unnecessary code, and being a beginner at thatFathomless
M
16
 const reducer = (state = initialState, { type, payload }) => {

   switch (type) {
      case RESET_STORE: {
        state = initialState
      }
        break
   }

   return state
 }

You can also fire an action which is handled by all or some reducers, that you want to reset to initial store. One action can trigger a reset to your whole state, or just a piece of it that seems fit to you. I believe this is the simplest and most controllable way of doing this.

Machicolate answered 25/11, 2016 at 12:24 Comment(0)
N
13

With Redux if have applied the following solution, which assumes I have set an initialState in all my reducers (e.g. { user: { name, email }}). In many components I check on these nested properties, so with this fix, I prevent my renders methods are broken on coupled property conditions (e.g. if state.user.email, which will throw an error user is undefined if the upper mentioned solutions).

const appReducer = combineReducers({
  tabs,
  user
})

const initialState = appReducer({}, {})

const rootReducer = (state, action) => {
  if (action.type === 'LOG_OUT') {
    state = initialState
  }

  return appReducer(state, action)
}
Neuritis answered 5/7, 2016 at 9:7 Comment(0)
P
8

UPDATE NGRX4

If you are migrating to NGRX 4, you may have noticed from the migration guide that the rootreducer method for combining your reducers has been replaced with the ActionReducerMap method. At first, this new way of doing things might make resetting the state a challenge. It is actually straightforward, yet the way of doing this has changed.

This solution is inspired by the meta-reducers API section of the NGRX4 Github docs.

First, lets say your are combining your reducers like this using NGRX's new ActionReducerMap option:

//index.reducer.ts
export const reducers: ActionReducerMap<State> = {
    auth: fromAuth.reducer,
    layout: fromLayout.reducer,
    users: fromUsers.reducer,
    networks: fromNetworks.reducer,
    routingDisplay: fromRoutingDisplay.reducer,
    routing: fromRouting.reducer,
    routes: fromRoutes.reducer,
    routesFilter: fromRoutesFilter.reducer,
    params: fromParams.reducer
}

Now, let's say you want to reset the state from within app.module

//app.module.ts
import { IndexReducer } from './index.reducer';
import { StoreModule, ActionReducer, MetaReducer } from '@ngrx/store';
...
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
    return function(state, action) {

      switch (action.type) {
          case fromAuth.LOGOUT:
            console.log("logout action");
            state = undefined;
      }
  
      return reducer(state, action);
    }
  }

  export const metaReducers: MetaReducer<any>[] = [debug];

  @NgModule({
    imports: [
        ...
        StoreModule.forRoot(reducers, { metaReducers}),
        ...
    ]
})

export class AppModule { }

And that is basically one way to achieve the same affect with NGRX 4.

Persephone answered 7/9, 2017 at 15:22 Comment(0)
H
8

My workaround when working with typescript, built on top of Dan Abramov's answer (redux typings make it impossible to pass undefined to reducer as the first argument, so I cache initial root state in a constant):

// store

export const store: Store<IStoreState> = createStore(
  rootReducer,
  storeEnhacer,
)

export const initialRootState = {
  ...store.getState(),
}

// root reducer

const appReducer = combineReducers<IStoreState>(reducers)

export const rootReducer = (state: IStoreState, action: IAction<any>) => {
  if (action.type === "USER_LOGOUT") {
    return appReducer(initialRootState, action)
  }

  return appReducer(state, action)
}


// auth service

class Auth {
  ...

  logout() {
    store.dispatch({type: "USER_LOGOUT"})
  }
}
Hildredhildreth answered 12/6, 2018 at 14:9 Comment(0)
N
6

Simply have your logout link clear session and refresh the page. No additional code needed for your store. Any time you want to completely reset the state a page refresh is a simple and easily repeatable way to handle it.

Nutpick answered 26/2, 2016 at 15:40 Comment(3)
What if you use a middleware that syncs the store to localstorage? Then your approach doesn't work at all...Cryo
I don't really understand why people downvote answers like this.Scour
Why have people downvoted this? 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!Smail
B
5

Combining Dan Abramov's answer, Ryan Irilli's answer and Rob Moorman's answer, to account for keeping the router state and initializing everything else in the state tree, I ended up with this:

const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? {
    ...appReducer({}, {}),
    router: state && state.router || {}
  } : state, action);
Boggers answered 6/7, 2016 at 15:42 Comment(0)
C
5

If you are using redux-actions, here's a quick workaround using a HOF(Higher Order Function) for handleActions.

import { handleActions } from 'redux-actions';

export function handleActionsEx(reducer, initialState) {
  const enhancedReducer = {
    ...reducer,
    RESET: () => initialState
  };
  return handleActions(enhancedReducer, initialState);
}

And then use handleActionsEx instead of original handleActions to handle reducers.

Dan's answer gives a great idea about this problem, but it didn't work out well for me, because I'm using redux-persist.
When used with redux-persist, simply passing undefined state didn't trigger persisting behavior, so I knew I had to manually remove item from storage (React Native in my case, thus AsyncStorage).

await AsyncStorage.removeItem('persist:root');

or

await persistor.flush(); // or await persistor.purge();

didn't work for me either - they just yelled at me. (e.g., complaining like "Unexpected key _persist ...")

Then I suddenly pondered all I want is just make every individual reducer return their own initial state when RESET action type is encountered. That way, persisting is handled naturally. Obviously without above utility function (handleActionsEx), my code won't look DRY (although it's just a one liner, i.e. RESET: () => initialState), but I couldn't stand it 'cuz I love metaprogramming.

Carcass answered 26/4, 2018 at 11:6 Comment(1)
` persistor.purge().then(() => { persistor.flush(); persistor.pause(); persistor.persist(); });` Only this worked for meDavena
S
4

I've created a component to give Redux the ability to reset state, you just need to use this component to enhance your store and dispatch a specific action.type to trigger reset. The thought of implementation is the same as what Dan Abramov said in their answer.

Github: https://github.com/wwayne/redux-reset

Stubby answered 26/3, 2016 at 17:3 Comment(0)
E
4

I have created actions to clear state. So when I dispatch a logout action creator I dispatch actions to clear state as well.

User record action

export const clearUserRecord = () => ({
  type: CLEAR_USER_RECORD
});

Logout action creator

export const logoutUser = () => {
  return dispatch => {
    dispatch(requestLogout())
    dispatch(receiveLogout())
    localStorage.removeItem('auth_token')
    dispatch({ type: 'CLEAR_USER_RECORD' })
  }
};

Reducer

const userRecords = (state = {isFetching: false,
  userRecord: [], message: ''}, action) => {
  switch (action.type) {
    case REQUEST_USER_RECORD:
    return { ...state,
      isFetching: true}
    case RECEIVE_USER_RECORD:
    return { ...state,
      isFetching: false,
      userRecord: action.user_record}
    case USER_RECORD_ERROR:
    return { ...state,
      isFetching: false,
      message: action.message}
    case CLEAR_USER_RECORD:
    return {...state,
      isFetching: false,
      message: '',
      userRecord: []}
    default:
      return state
  }
};

I am not sure if this is optimal?

Exmoor answered 19/9, 2016 at 20:2 Comment(1)
why don't just return state in CLEAR_USER_RECORD case?Anders
K
4

My take to keep Redux from referencing to the same variable of the initial state:

// write the default state as a function
const defaultOptionsState = () => ({
  option1: '',
  option2: 42,
});

const initialState = {
  options: defaultOptionsState() // invoke it in your initial state
};

export default (state = initialState, action) => {

  switch (action.type) {

    case RESET_OPTIONS:
    return {
      ...state,
      options: defaultOptionsState() // invoke the default function to reset this part of the state
    };

    default:
    return state;
  }
};
Kero answered 10/4, 2019 at 0:6 Comment(1)
The idea to write the default state as a function really saved the day here. Thank you 🙏Buckra
C
3

The following solution worked for me.

I added resetting state function to meta reducers.The key was to use

return reducer(undefined, action);

to set all reducers to initial state. Returning undefined instead was causing errors due to the fact that the structure of the store has been destroyed.

/reducers/index.ts

export function resetState(reducer: ActionReducer<State>): ActionReducer<State> {
  return function (state: State, action: Action): State {

    switch (action.type) {
      case AuthActionTypes.Logout: {
        return reducer(undefined, action);
      }
      default: {
        return reducer(state, action);
      }
    }
  };
}

export const metaReducers: MetaReducer<State>[] = [ resetState ];

app.module.ts

import { StoreModule } from '@ngrx/store';
import { metaReducers, reducers } from './reducers';

@NgModule({
  imports: [
    StoreModule.forRoot(reducers, { metaReducers })
  ]
})
export class AppModule {}
Curvilinear answered 7/5, 2018 at 10:16 Comment(0)
H
2

Dan Abramov's answer helped me solve my case. However, I encountered a case where not the entire state had to be cleared. So I did it this way:

const combinedReducer = combineReducers({
    // my reducers 
});

const rootReducer = (state, action) => {
    if (action.type === RESET_REDUX_STATE) {
        // clear everything but keep the stuff we want to be preserved ..
        delete state.something;
        delete state.anotherThing;
    }
    return combinedReducer(state, action);
}

export default rootReducer;
Hasidism answered 22/11, 2018 at 18:36 Comment(1)
what if I have more than 10 states but I want to reset state of just one reducer?Melicent
V
1

Just an extension to @dan-abramov answer, sometimes we may need to retain certain keys from being reset.

const retainKeys = ['appConfig'];

const rootReducer = (state, action) => {
  if (action.type === 'LOGOUT_USER_SUCCESS' && state) {
    state = !isEmpty(retainKeys) ? pick(state, retainKeys) : undefined;
  }

  return appReducer(state, action);
};
Vaunt answered 13/3, 2018 at 1:56 Comment(0)
R
0

This approach is very right: Destruct any specific state "NAME" to ignore and keep others.

const rootReducer = (state, action) => {
    if (action.type === 'USER_LOGOUT') {
        state.NAME = undefined
    }
    return appReducer(state, action)
}
Rabelaisian answered 31/5, 2016 at 8:25 Comment(1)
If you only need to reset one piece of your state tree, you could also listen for USER_LOGOUT in that reducer and handle it there.Boggers
S
0

I found that Dan Abramov's answer worked well for me, but it triggered the ESLint no-param-reassign error - https://eslint.org/docs/rules/no-param-reassign

Here's how I handled it instead, making sure to create a copy of the state (which is, in my understanding, the Reduxy thing to do...):

import { combineReducers } from "redux"
import { routerReducer } from "react-router-redux"
import ws from "reducers/ws"
import session from "reducers/session"
import app from "reducers/app"

const appReducer = combineReducers({
    "routing": routerReducer,
    ws,
    session,
    app
})

export default (state, action) => {
    const stateCopy = action.type === "LOGOUT" ? undefined : { ...state }
    return appReducer(stateCopy, action)
}

But maybe creating a copy of the state to just pass it into another reducer function that creates a copy of that is a little over-complicated? This doesn't read as nicely, but is more to-the-point:

export default (state, action) => {
    return appReducer(action.type === "LOGOUT" ? undefined : state, action)
}
Skysail answered 4/6, 2018 at 20:15 Comment(0)
L
0

First on initiation of our application the reducer state is fresh and new with default InitialState.

We have to add an action that calls on APP inital load to persists default state.

While logging out of the application we can simple reAssign the default state and reducer will work just as new.

Main APP Container

  componentDidMount() {   
    this.props.persistReducerState();
  }

Main APP Reducer

const appReducer = combineReducers({
  user: userStatusReducer,     
  analysis: analysisReducer,
  incentives: incentivesReducer
});

let defaultState = null;
export default (state, action) => {
  switch (action.type) {
    case appActions.ON_APP_LOAD:
      defaultState = defaultState || state;
      break;
    case userLoginActions.USER_LOGOUT:
      state = defaultState;
      return state;
    default:
      break;
  }
  return appReducer(state, action);
};

On Logout calling action for resetting state

function* logoutUser(action) {
  try {
    const response = yield call(UserLoginService.logout);
    yield put(LoginActions.logoutSuccess());
  } catch (error) {
    toast.error(error.message, {
      position: toast.POSITION.TOP_RIGHT
    });
  }
}
Luciennelucier answered 18/8, 2018 at 14:25 Comment(0)
M
0

A quick and easy option that worked for me was using redux-reset . Which was straightforward and also has some advanced options, for larger apps.

Setup in create store

import reduxReset from 'redux-reset'
// ...
const enHanceCreateStore = compose(
    applyMiddleware(...),
    reduxReset()  // Will use 'RESET' as default action.type to trigger reset
)(createStore)
const store = enHanceCreateStore(reducers)

Dispatch your 'reset' in your logout function

store.dispatch({
    type: 'RESET'
})
Mayhap answered 17/1, 2019 at 9:1 Comment(0)
L
0

For me to reset the state to its initial state, I wrote the following code:

const appReducers = (state, action) =>
   combineReducers({ reducer1, reducer2, user })(
     action.type === "LOGOUT" ? undefined : state,
     action
);
Lakesha answered 13/2, 2019 at 16:10 Comment(0)
B
0

One thing Dan Abramov's answer doesn't do is clear the cache for parameterized selectors. If you have a selector like this:

export const selectCounter1 = (state: State) => state.counter1;
export const selectCounter2 = (state: State) => state.counter2;
export const selectTotal = createSelector(
  selectCounter1,
  selectCounter2,
  (counter1, counter2) => counter1 + counter2
);

Then you would have to release them on logout like this:

selectTotal.release();

Otherwise, the memoized value for the last call of the selector and the values of the last parameters will still be in memory.

Code samples are from the ngrx docs.

Bonni answered 31/10, 2019 at 9:33 Comment(1)
You are posting an answer about RxJS, but OP asking for Redux. Logic is same tbh.Togoland
M
0

Approach with Redux Toolkit:


export const createRootReducer = (history: History) => {
  const rootReducerFn = combineReducers({
    auth: authReducer,
    users: usersReducer,
    ...allOtherReducers,
    router: connectRouter(history),
  });

  return (state: Parameters<typeof rootReducerFn>[0], action: Parameters<typeof rootReducerFn>[1]) =>
    rootReducerFn(action.type === appActions.reset.type ? undefined : state, action);
};

Marmoreal answered 17/10, 2021 at 22:0 Comment(0)
S
-1

why don't you just use return module.exports.default() ;)

export default (state = {pending: false, error: null}, action = {}) => {
    switch (action.type) {
        case "RESET_POST":
            return module.exports.default();
        case "SEND_POST_PENDING":
            return {...state, pending: true, error: null};
        // ....
    }
    return state;
}

Note: make sure you set action default value to {} and you are ok because you don't want to encounter error when you check action.type inside the switch statement.

Subtilize answered 2/12, 2016 at 17:8 Comment(0)
T
-1

Another option is to:

store.dispatch({type: '@@redux/INIT'})

'@@redux/INIT' is the action type that redux dispatches automatically when you createStore, so assuming your reducers all have a default already, this would get caught by those and start your state off fresh. It might be considered a private implementation detail of redux, though, so buyer beware...

Teplica answered 30/5, 2017 at 16:26 Comment(1)
I did that it's not changing state, also I tried @@INIT which is shown in ReduxDevtools as first actionMontparnasse
V
-1

In addition to Dan Abramov's answer, shouldn't we explicitly set action as action = {type: '@@INIT'} alongside state = undefined. With above action type, every reducer returns the initial state.

Vish answered 14/7, 2018 at 9:50 Comment(0)
T
-1

In the server, I have a variable:global.isSsr = true and in each reducer, I have a const: initialState To reset the data in the Store, I do the following with each Reducer:

Example with appReducer.js:

 const initialState = {
    auth: {},
    theme: {},
    sidebar: {},
    lsFanpage: {},
    lsChatApp: {},
    appSelected: {},
};

export default function (state = initialState, action) {
    if (typeof isSsr!=="undefined" && isSsr) { //<== using global.isSsr = true
        state = {...initialState};//<= important "will reset the data every time there is a request from the client to the server"
    }
    switch (action.type) {
        //...other code case here
        default: {
            return state;
        }
    }
}

Finally on the server's router:

router.get('*', (req, res) => {
        store.dispatch({type:'reset-all-blabla'});//<= unlike any action.type // i use Math.random()
        // code ....render ssr here
});
Toluol answered 11/8, 2018 at 6:38 Comment(0)
T
-1

for me what worked the best is to set the initialState instead of state:

  const reducer = createReducer(initialState,
  on(proofActions.cleanAdditionalInsuredState, (state, action) => ({
    ...initialState
  })),
Timecard answered 19/3, 2020 at 11:27 Comment(0)
E
-1

If you want to reset a single reducer

For example

const initialState = {
  isLogged: false
}
//this will be your action
export const resetReducer = () => {
  return {
    type: "RESET"
  }
}

export default (state = initialState, {
  type,
  payload
}) => {
  switch (type) {
    //your actions will come her
    case "RESET":
      return {
        ...initialState
      }
  }
}

//and from your frontend
dispatch(resetReducer())
Enwrap answered 18/7, 2020 at 7:42 Comment(0)
M
-1

You can null the reducers' data by adding this code to action file,

import all types first:

import * as types from './types';

add this code to logout action

for(let key of Object.values(types)) {
        dispatch({ type: key, payload: [] });
    }
Martens answered 30/11, 2020 at 9:12 Comment(1)
I think that it's a bit overkill to dispatch an action for each store key, to reset each state. Other solutions related to root reducer look better.Lordan
O
-1
npm install redux-reset
import reduxReset from 'redux-reset'
...
const enHanceCreateStore = compose(
    applyMiddleware(...),
    reduxReset()  // Will use 'RESET' as default action.type to trigger reset
  )(createStore)
const store = enHanceCreateStore(reducers)

https://github.com/wwayne/redux-reset
Overspread answered 29/7, 2021 at 8:49 Comment(0)
K
-2

just edit the file where the reducers are declared

import { combineReducers } from 'redux';

import gets from '../';

const rootReducer = (state, action) => {
  let asReset = action.type === 'RESET_STORE';

  const reducers = combineReducers({
    gets,
  });

  const transition = {
    true() {
      return reducers({}, action);
    },
    false() {
      return reducers(state, action);
    },
  };
  return transition[asReset] && transition[asReset]();
};

export default rootReducer;
Keniakenilworth answered 30/7, 2021 at 21:21 Comment(0)
E
-4

onLogout() {
  this.props.history.push('/login'); // send user to login page
  window.location.reload(); // refresh the page
}
Electrophilic answered 31/10, 2019 at 19:46 Comment(4)
explain? @SinanSametElectrophilic
To be clear I did not down vote this. But I do discourage it. If you navigate to login and reload you will probably logout yes. But only because you lost your state. Unless you use redux-persist in which case you won't even logout. Overall, I just hate seeing functions like window.location.reload();Progress
- Do not write functions like this. - Why? - I don't like it.Electrophilic
Reloading the page is different from resetting store state. Besides, you might be in an environment without window, e.g. react-nativeScabbard

© 2022 - 2024 — McMap. All rights reserved.