我正在使用Redux进行状态管理。 如何将存储重置为初始状态?

例如,假设我有两个用户帐户(u1和u2)。 想象下面的一系列事件:

用户u1登录到应用程序并做了一些事情,所以我们在存储中缓存一些数据。 用户u1退出。 用户u2无需刷新浏览器即可登录应用。

此时,缓存的数据将与u1关联,我想清理它。

当第一个用户注销时,如何将Redux存储重置为初始状态?


当前回答

下面的解决方案对我很有效。

我添加了重置状态功能到元还原器。关键在于使用

return reducer(undefined, action);

将所有减速器设置为初始状态。返回undefined反而会导致错误,因为存储的结构已经被破坏。

/还原剂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 {}

其他回答

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.

(当然,作为共享计算机上的用户,你应该使用“私人浏览”模式,自己关闭浏览器窗口,使用“清除浏览数据”功能,等等,但作为开发人员,我们不能期望每个人都总是那么勤奋)

一种方法是在应用程序中编写一个根减速器。

根减速机通常会将处理操作委托给combineReducers()生成的减速机。但是,无论何时它接收到USER_LOGOUT操作,它都会再次返回初始状态。

例如,如果你的根减速器是这样的:

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

你可以将它重命名为appReducer,并编写一个新的rootReducer委托给它:

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

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

现在我们只需要教新的rootReducer返回初始状态以响应USER_LOGOUT操作。如我们所知,无论操作如何,当调用以undefined作为第一个参数时,约简器都应该返回初始状态。让我们用这个事实来有条件地剥离累积状态,当我们把它传递给appReducer:

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

  return appReducer(state, action)
}

现在,每当USER_LOGOUT触发时,所有减约器都将重新初始化。它们还可以返回与初始值不同的值,因为它们可以检查动作。也要打字。

重申一下,完整的新代码是这样的:

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)
}

如果使用redux-persist,可能还需要清理存储空间。Redux-persist将您的状态副本保存在存储引擎中,刷新时将从那里加载状态副本。

首先,您需要导入适当的存储引擎,然后在将其设置为undefined并清除每个存储状态键之前解析状态。

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);
};
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

如果你正在使用还原动作,这里有一个快速的解决方案,使用HOF(高阶函数)为handleActions。

import { handleActions } from 'redux-actions';

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

然后使用handleActionsEx代替原来的handleActions来处理reducers。

Dan的回答给出了关于这个问题的一个很好的想法,但它对我来说并不是很有效,因为我使用的是还原坚持。 当使用redux-persist时,简单地传递未定义的状态不会触发持久化行为,所以我知道我必须手动从存储中删除项目(在我的情况下,React Native,因此AsyncStorage)。

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

or

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

对我也没用——他们只是对我大喊大叫。(例如,抱怨“意外的键_persist…”)

然后我突然思考,我想要的只是让每个减速机在遇到RESET动作类型时回到自己的初始状态。这样,持久化就被自然地处理了。显然没有上面的实用函数(handleActionsEx),我的代码不会看起来很干(虽然它只是一个一行程序,即RESET: () => initialState),但我不能容忍它,因为我喜欢元编程。

简单回答一下丹·阿布拉莫夫的问题:

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


export default (state, action) =>
  rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);