How to reset the state of a Redux store?

2019-01-01 01:20发布

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?

24条回答
牵手、夕阳
2楼-- · 2019-01-01 02:05

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.

查看更多
呛了眼睛熬了心
3楼-- · 2019-01-01 02:05

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...

查看更多
大哥的爱人
4楼-- · 2019-01-01 02:07

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 ActionReducerMap method. At first, this new way of doing things might make resetting state a challenge. It is actually straight-forward, 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, lets say you want to reset 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.

查看更多
春风洒进眼中
5楼-- · 2019-01-01 02:07

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 {}
查看更多
孤独总比滥情好
6楼-- · 2019-01-01 02:07

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)

查看更多
无色无味的生活
7楼-- · 2019-01-01 02:08

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.

查看更多
登录 后发表回答