如何重置Redux商店的状态?

我正在使用Redux进行状态pipe理。
如何将商店重置为初始状态?

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

  1. 用户u1login到应用程序并执行某些操作,因此我们将一些数据caching在商店中。

  2. 用户u1注销。

  3. 用户u2login到应用程序,而无需刷新浏览器。

在这一点上,caching的数据将与u1相关联,我想清理它。

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

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

根减速器通常将处理动作委托给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) } 

现在我们只需要教导新的rootReducerUSER_LOGOUT操作之后返回初始状态。 正如我们所知道的那样,reducer在返回初始状态的时候,用undefined作为第一个参数被调用,不pipe这个动作是什么。 让我们使用这个事实来有条件地appReducer积累的state因为我们把它传递给appReducer

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

现在,每当USER_LOGOUT触发时,所有的减速器将被重新初始化。 如果他们想要,他们也可以返回与最初不同的东西,因为他们也可以检查action.type

重申一下,完整的新代码如下所示:

 const appReducer = combineReducers({ /* your app's top-level reducers */ }) const rootReducer = (state, action) => { if (action.type === 'USER_LOGOUT') { state = undefined } return appReducer(state, action) } 

请注意,我不是在这里改变状态,我只是在将它传递给另一个函数之前, 重新分配称为state的局部variables的引用 。 变换状态对象将违反Redux原则。

我想指出,丹·阿布拉莫夫接受的评论是正确的,除了我们在使用react-router-redux软件包时遇到了一个奇怪的问题。 我们的修复是不设置状态为undefined ,而是仍然使用当前的路由减速器。 所以如果你使用这个软件包,我会build议你实施下面的解决scheme

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

使用Redux如果已经应用了以下解决scheme,该scheme假设我已经在所有缩减器(例如{user:{name,email}})中设置了一个initialState。 在许多组件中,我检查这些嵌套的属性,所以这个修复我防止我的渲染方法打破了耦合属性条件(例如,如果state.user.email,将抛出一个错误的用户是不确定的,如果上面提到的解决scheme)。

 const appReducer = combineReducers({ tabs, user }) const initialState = appReducer({}, {}) const rootReducer = (state, action) => { if (action.type === 'LOG_OUT') { state = initialState } return appReducer(state, action) } 
  const reducer = (state = initialState, { type, payload }) => { switch (type) { case RESET_STORE: { state = initialState } break } return state } 

您还可以触发由所有或部分减速器处理的行动,您要重置为初始存储。 一个动作可以触发重置到你的整个状态,或者只是一个似乎适合你的状态。 我相信这是做这件事最简单,最可控的方法。

结合Dan,Ryan和Rob的方法来说明保持router状态并初始化状态树中的所有内容,结果是:

 const rootReducer = (state, action) => appReducer(action.type === LOGOUT ? { ...appReducer({}, {}), router: state && state.router || {} } : state, action); 

我已经创build了一个组件给Redux重置状态的能力,你只需要使用这个组件来增强你的存储和调度一个特定的action.type来触发重置。 执行的思想和丹·阿布拉莫夫所说的一样。

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

更新NGRX4

如果您正在迁移到NGRX 4,您可能已经注意到迁移指南中已经使用ActionReducerMap方法replace了用于组合Reducer的rootreducer方法。 起初,这种做事方式可能会使重置状态成为一个挑战。 这实际上是直接的,但是这样做的方式已经改变了。

此解决scheme受NGRX4 Github文档的meta- redurs API部分的启发。

首先,让我们说你正在使用NGRX的新ActionReducerMap选项来结合你的减速器:

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

现在,让我们说你想从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 { } 

`

这基本上是与NGRX 4达成同样效果的一种方式。

这种方法是非常正确的:摧毁任何特定的状态“名称”,忽视和保留他人。

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

我已经创build了清除状态的操作。 所以当我派遣一个注销动作创build者时,我也派遣一些动作来清除状态。

用户logging操作

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

注销动作创build者

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

减速器

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

我不确定这是否是最佳的?

你为什么不使用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; } 

注意:确保将操作默认值设置为{}并且您确定,因为您在switch语句中检查action.type时不想遇到错误。

另一个select是:

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

'@@redux/INIT'是您在createStore时自动调度的动作types,所以假设您的缩减器都已经有一个默认值,那么这个动作就会被这些动作捕捉到,并且启动你的状态。 尽pipe如此,它可能被认为是re​​dux的一个私有实现细节,所以购买者要小心…

只需将您的注销链接清除会话并刷新页面即可。 没有额外的代码需要您的商店。 任何时候你想完全重置状态页面刷新是一个简单的,容易重复的方式来处理它。