我正在使用Redux进行状态管理 .
如何将商店重置为初始状态?
例如,假设我有两个用户帐户( u1
和 u2
) .
想象一下以下一系列事件:
-
用户
u1
登录到应用程序并执行某些操作,因此我们会在商店中缓存一些数据 . -
用户
u1
注销 . -
用户
u2
登录应用程序而不刷新浏览器 .
此时,缓存的数据将与 u1
关联,我想清理它 .
How can I reset the Redux store to its initial state when the first user logs out?
24 回答
从安全角度来看,记录用户时最安全的做法是重置所有持久状态(e.x. cookies,
localStorage
,IndexedDB
,Web SQL
等)并使用window.location.reload()
对页面进行硬刷新 . 一个草率的开发人员可能会意外地或有意地将一些敏感数据存储在DOM中的window
等 . 吹掉所有持久状态并刷新浏览器是保证以前用户的信息不会泄露给下一个用户的唯一方法 .(当然,作为共享计算机上的用户,您应该使用“隐私浏览”模式,自己关闭浏览器窗口,使用“清除浏览数据”功能等,但作为开发人员,我们不能指望每个人都是那勤奋)
除了Dan Abramov的答案之外,我们不应该明确将行动设置为action = {type:'@@ INIT'}以及state = undefined . 使用上述操作类型,每个reducer返回初始状态 .
我在使用打字稿时的解决方法, Build 在Dan的答案之上(redux typings使得无法将
undefined
作为第一个参数传递给reducer,所以我将初始根状态缓存在常量中):定义一个动作:
然后在每个reducers中假设您使用
switch
或if-else
来处理通过每个reducer的多个操作 . 我将以switch
为例 .这样,无论何时调用
RESET
action,reducer都会以默认状态更新存储 .现在,对于注销,您可以处理以下内容:
每次用户登录时,都不会刷新浏览器 . 商店将始终处于默认状态 .
store.dispatch(RESET_ACTION)
刚刚阐述了这个想法 . 你很可能会有一个动作创建者 . 一个更好的方法是你有一个LOGOUT_ACTION
.一旦你发出这个
LOGOUT_ACTION
. 然后,自定义中间件可以使用Redux-Saga或Redux-Thunk拦截此操作 . 但是,无论如何,您都可以发送另一个动作'RESET' . 这样,商店注销和重置将同步进行,您的商店将准备好进行其他用户登录 .我想指出Dan Abramov接受的评论是正确的,除非我们在使用react-router-redux软件包以及这种方法时遇到了一个奇怪的问题 . 我们的修复是不将状态设置为
undefined
,而是仍然使用当前路由减少器 . 因此,如果您使用此软件包,我建议您实施以下解决方案这种方法非常正确:破坏任何特定状态“NAME”以忽略并保留其他状态 .
使用Redux if已应用以下解决方案,假设我已在所有reducers中设置了initialState(例如{user:{name,email}}) . 在许多组件中,我检查这些嵌套属性,因此使用此修复程序,我阻止我的渲染方法在耦合属性条件下被破坏(例如,如果上面提到的解决方案,则将抛出错误用户的state.user.email未定义) .
只是简单回答最佳答案:
如果您使用的是redux-actions,则可以使用HOF(高阶函数)快速解决
handleActions
.然后使用
handleActionsEx
而不是原始handleActions
来处理reducer .Dan's answer给出了一个关于这个问题的好主意,但它并没有't work out well for me, because I'使用
redux-persist
.当与
redux-persist
一起使用时,简单地传递undefined
状态不会触发持久行为,所以我知道我必须手动从存储中删除项目(在我的情况下是React Native,因此AsyncStorage
) .要么
也不适合我 - 他们只是对我大吼大叫 . (例如抱怨 "Unexpected key _persist ..." )
然后我突然思考我想要的只是让每个减速器在遇到
RESET
动作类型时返回自己的初始状态 . 这样,坚持自然处理 . 显然没有上面的效用函数(handleActionsEx
),我的代码只赢了't look DRY (although it'只是一个班轮,即RESET: () => initialState
),但我不能't stand it '因为我喜欢元编程 .您还可以触发由要重置为初始存储的所有或部分缩减器处理的操作 . 一个动作可以触发重置到整个状态,或只是一个看起来适合你的状态 . 我相信这是最简单,最可控的方式 .
我已经创建了清除状态的动作 . 因此,当我发送一个注销动作创建者时,我也会调度动作来清除状态 .
User record action
Logout action creator
Reducer
我不确定这是否是最佳的?
更新NGRX4
如果要迁移到NGRX 4,您可能已经注意到migration guide用于组合reducers的rootreducer方法已被ActionReducerMap方法替换 . 起初,这种新的做事方式可能会使重置状态成为挑战 . 它实际上是直截了当的,但这样做的方式已经改变 .
此解决方案的灵感来自NGRX4 Github docs.的meta-reducers API部分
首先,假设您正在使用NGRX的新ActionReducerMap选项组合您的Reducer:
现在,假设您要从app.module中重置状态
`
这基本上是与NGRX 4实现相同效果的一种方法 .
接受的答案帮助我解决了我的问题 . 但是,我遇到了必须清除整个州的情况 . 所以 - 我这样做了:
希望这可以帮助别人:)
我已经创建了一个组件来为Redux提供重置状态的能力,你只需要使用这个组件来增强你的存储并调度一个特定的action.type来触发重置 . 实施的想法与@Dan Abramov所说的相同 .
Github:https://github.com/wwayne/redux-reset
以下解决方案适合我 .
我添加了重置状态函数到meta redurs . 关键是使用
将所有Reducer设置为初始状态 . 由于存储的结构已被破坏,因此返回
undefined
导致错误 ./reducers/index.ts
app.module.ts
只是@dan-abramov答案的扩展,有时我们可能需要保留某些键不被重置 .
以下解决方案适合我 .
首先在我们的应用程序的 initiation 上,reducer状态是 fresh 和 new ,默认为 InitialState .
我们必须添加一个调用APP初始加载的动作来持续 default state .
在退出应用程序时,我们可以简单 default state default state 和reducer将像 new 一样工作 .
Main APP Container
Main APP Reducer
On Logout calling action for resetting state
Hope this solves your problem!
另一种选择是:
'@@redux/INIT'
是redux在你createStore
时自动调度的动作类型,所以假设你的减速器都已经有一个默认值,那么这将被它们捕获并开始你的状态 . 它可能被认为是redux的私人实现细节,所以买家要小心......你为什么不用
return module.exports.default()
;)Note: 确保将操作默认值设置为
{}
并且您没问题,因为您在switch语句中检查action.type
时不希望遇到错误 .在服务器中,我有一个变量是: global.isSsr = true 并且在每个reducer中,我有一个const是: initialState 要重置Store中的数据,我对每个Reducer执行以下操作: example with appReducer.js :
finally on the server's router:
我发现接受的答案对我来说效果很好,但它触发了ESLint
no-param-reassign
错误 - https://eslint.org/docs/rules/no-param-reassign这是我如何处理它,确保创建一个状态的副本(在我的理解中,这是要做的Reduxy的事情......):
但也许创建一个状态的副本,只是将它传递到另一个reducer函数,创建一个副本,有点过于复杂?这看起来不太好,但更重要的是:
只需将您的注销链接清除会话并刷新页面即可 . 您的商店不需要其他代码 . 只要您想完全重置状态,页面刷新就是一种简单且易于重复的处理方式 .
结合Dan,Ryan和Rob的方法来考虑保持
router
状态并初始化状态树中的其他所有内容,我最终得到了这样的结论:一种方法是在应用程序中编写根reducer .
根减速器通常会将操作委托给
combineReducers()
生成的reducer . 但是,只要它收到USER_LOGOUT
动作,它就会重新返回初始状态 .例如,如果您的根减速器看起来像这样:
您可以将其重命名为
appReducer
并为其写一个新的rootReducer
委托:现在我们只需要教新的
rootReducer
以在USER_LOGOUT
动作之后返回初始状态 . 我们知道,无论动作如何,当使用undefined
作为第一个参数调用它们时,reducers应该返回初始状态 . 让我们使用这个事实有条件地剥离累积的state
,因为我们将它传递给appReducer
:现在,只要
USER_LOGOUT
触发,所有减速器都将重新初始化 . 如果他们愿意,他们也可以返回与他们最初不同的东西,因为他们也可以检查action.type
.重申一下,全新代码如下所示:
请注意,我不是在这里改变状态,在传递给另一个函数之前,我只是一个名为
state
的局部变量的reassigning the reference . 改变状态对象将违反Redux原则 .如果使用redux-persist,您可能还需要清理存储空间 . Redux-persist会将您的状态副本保存在存储引擎中,并在刷新时从那里加载它们 .
首先,您需要导入相应的storage engine,然后在将其设置为
undefined
之前解析状态并清除每个存储状态键 .