如何重置Redux商店的状态?
我正在使用Redux进行状态pipe理。
如何将商店重置为初始状态?
例如,假设我有两个用户帐户( u1
和u2
)。
想象下面的一系列事件:
-
用户
u1
login到应用程序并执行某些操作,因此我们将一些数据caching在商店中。 -
用户
u1
注销。 -
用户
u2
login到应用程序,而无需刷新浏览器。
在这一点上,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) }
现在我们只需要教导新的rootReducer
在USER_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如此,它可能被认为是redux的一个私有实现细节,所以购买者要小心…
只需将您的注销链接清除会话并刷新页面即可。 没有额外的代码需要您的商店。 任何时候你想完全重置状态页面刷新是一个简单的,容易重复的方式来处理它。