我正在使用 Redux 进行状态管理。
如何将商店重置为初始状态?
例如,假设我有两个用户帐户 (u1
和u2
)。
想象以下事件序列:
用户
u1
登录应用程序并执行某些操作,因此我们在商店中缓存了一些数据。用户
u1
注销。用户
u2
无需刷新浏览器即可登录应用程序。
此时缓存的数据会关联到u1
,我想清理一下。
当第一个用户注销时,如何将 Redux 存储重置为其初始状态?
我正在使用 Redux 进行状态管理。
如何将商店重置为初始状态?
例如,假设我有两个用户帐户 (u1
和u2
)。
想象以下事件序列:
用户u1
登录应用程序并执行某些操作,因此我们在商店中缓存了一些数据。
用户u1
注销。
用户u2
无需刷新浏览器即可登录应用程序。
此时缓存的数据会关联到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)
}
现在我们只需要教 newrootReducer
返回初始状态以响应USER_LOGOUT
动作。正如我们所知,undefined
无论操作如何,reducer 在被调用时都应该返回初始状态作为第一个参数。state
当我们将它传递给 时,让我们使用这个事实来有条件地剥离累积appReducer
:
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
return appReducer(undefined, action)
}
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') {
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);
};
Dan Abramov的回答是正确的,只是我们在使用 react-router-redux 包和这种方法时遇到了一个奇怪的问题。
我们的修复是不将状态设置为undefined
而是仍然使用当前的路由减少器。因此,如果您正在使用此软件包,我建议您实施以下解决方案
const rootReducer = (state, action) => {
if (action.type === 'USER_LOGOUT') {
const { routing } = state
state = { routing }
}
return appReducer(state, action)
}
定义一个动作:
const RESET_ACTION = {
type: "RESET"
}
然后在假设您正在使用switch
或if-else
通过每个减速器处理多个操作的每个减速器中。我要为一个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;
}
}
这样,无论何时调用RESET
action,reducer 都会用默认状态更新 store。
现在,对于注销,您可以处理如下:
const logoutHandler = () => {
store.dispatch(RESET_ACTION)
// Also the custom logic like for the rest of the logout handler
}
每次用户登录时,无需刷新浏览器。商店将始终处于默认状态。
store.dispatch(RESET_ACTION)
只是阐述了这个想法。为此,您很可能会有一个动作创建者。更好的方法是你有一个LOGOUT_ACTION
.
一旦你派遣这个LOGOUT_ACTION
. 然后,自定义中间件可以使用 Redux-Saga 或 Redux-Thunk 拦截此操作。但是,这两种方式都可以发送另一个动作“RESET”。这样,商店注销和重置将同步发生,您的商店将为另一个用户登录做好准备。
只是对Dan Abramov的回答的简化回答:
const rootReducer = combineReducers({
auth: authReducer,
...formReducers,
routing
});
export default (state, action) =>
rootReducer(action.type === 'USER_LOGOUT' ? undefined : state, action);
使用Redux Toolkit和/或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);
};