将React用于短暂状态,该状态对应用程序全局无关紧要,并且不会以复杂的方式进行更改。 例如,在某些UI元素中切换,即表单输入状态。 将Redux用于全局重要的状态或以复杂方式突变的状态。 例如,缓存的用户或后期草稿。
有时,您可能希望从Redux状态转换为React状态(当在Redux中存储内容变得笨拙时),或者反过来(当更多组件需要访问某些曾经是本地的状态时)。
经验法则是:做些不太尴尬的事情。
对于通过网络请求获取的数据,请始终使用商店以支持服务器端呈现(如果您认为这是最终目标),否则将无法补充水分。
对于那些要被两个或更多容器监听的状态,它也应该在存储中吗?
我同意@gaeron关于短暂与持久的区别。 但是我实际上从三个方面考虑。 一种是他谈论的UI状态,我也认为这是短暂的。 另一个是应用程序状态,它实际上是路由状态。 我使用“路由”一词是因为它为人们所熟悉,但是我将其抽象为“视图选择”状态,因为我认为它更好地涵盖了台式机,Web和移动设备。
现在您可以说这是UI状态,因为它决定了人们看到的内容(例如,非常类似于选项卡的状态)。 但是我看到两个区别。 首先是状态被序列化(即作为URL)并发送给其他人。 因此,如果您希望人们能够“深层链接”到该特定UI状态,则事情应该进入路由状态。 第二个原因是,在许多情况下,初始路由状态或路由更改会触发应用程序状态更改(即,加载要查看的数据)。 当然,UI中的操作也可以执行相同的操作。 但是我的区别是,您可以(并且应该)拥有路由状态而无需任何视图或渲染,以精确地测试围绕路由状态变化的“应用程序逻辑” 。 这是因为不需要涉及视图和渲染,从而使其部分成为应用程序状态,恕我直言。
这与Redux与状态管理的React问题有什么关系? 应用程序状态是Redux的域,UI状态是React的域。 但是,路由状态(在我看来)应该由Redux管理,即使可以将其视为UI状态(请参阅嵌入式链接以获取有关我为什么这样的更多讨论)。
需要明确的是, @ gaearon对周围事物的评论仍然适用。 但是我发现区分这些不同的情况很有用。
应用程序状态是Redux的域,UI状态是React的域。
请注意,我没有要求。 我认为在React中保留一些应用程序状态以及在Redux中保留一些UI状态都很好。 我不认为它们应该被域分开。
我的思考方式是,如果您使用Redux创建应用,请接受单一状态树。 也将UI状态放在那里。 但是,如果它变得乏味且令人沮丧,请不要担心将状态放入组件中。 我的观点是,除非有尴尬,否则请使用单状态树,并且仅在为您简化事情而不是使事情复杂化时才这样做。 那是唯一的准则。
首先,我当然不是想在@gaearon的嘴里放些话,对此感到抱歉。
其次,我完全同意@gaearon。 我也坚信单一状态树方法。 当我谈论UI状态时,我脑海中确实有一些小事(例如,当前选择的选项卡是什么),而实际上与应用程序状态无关(实际上讨论了
因此,让我澄清一下我的立场。 我同意几乎所有内容都应该在Redux中,包括路由状态(就像我在Reddo和TypeScript的TodoMVC实现中所做的那样。我特别提到了路由状态,并区分了它是因为我认为(您可以在该TodoMVC中看到它)实现),它绝对属于Redux(并且根本不应该与渲染连接),并且绝对不是“ UI状态”。
对于React组件,我很少使用状态。 我更喜欢使用道具来获取有关渲染内容的信息,以及获取可以调用以触发组件外部更改的闭包。 在少数情况下,我在组件中引入状态只是为了使生活更轻松,但我尝试避免这种情况。 我希望这可以澄清问题。
我认为这个问题确实是主观且复杂的,因此我今天与团队做出了艰难的决定,不要打扰:
this.setState
。现在,我们正在实现一些帮助程序,以减少管理微小的UI状态的麻烦。
@gaearon @ lionng429 @xogeny您能想到这种方法有什么弊端吗?
@inetfuture我倾向于对应用程序(redux)状态有些combineReducers
和connect
来挂接东西(至少保留类型约束)。 因此,这可能不是代表性的观点。 但是我会说为什么您说“将所有东西都放入Redux商店”,我会担心您最终会陷入困境。 我认为使用TypeScript意味着需要花一些精力来扩展应用程序状态这一事实并不一定是一件坏事,因为它迫使我决定“我真的需要这个吗?” 而不是堆放东西。
另一个想法是:本地组件状态对于需要快速更新时间的受控输入很有用,并且可以减少触发的实际操作的数量。 在当前的原型中,我整理了一个表单编辑HOC,将正在编辑的当前项目作为道具。 它还通过以下方式处理表单输入更改事件:将更改的字段保存在其状态中,然后使用合并的本地WIP更改调用去抖动的“ EDIT_ITEM_ATTRIBUTE”动作创建者。 结果是表单字段立即更新,因为仅表单本身正在重新呈现,并且触发的动作少得多(例如,如果我按住“ A”几秒钟,则只有“ AAAAAAAAAAAA”值会作为动作而不是“ A”,“ AA”等发送)。
如果有人关心,我已经在https://gist.github.com/markerikson/554cab15d83fd994dfab上提供了HOC的要点。
无论如何,关键是组件状态和存储状态都有其用途-您只需要考虑实际的用例即可。
对于可重用的组件,如果它们足够大且复杂,则将它们的状态保留在Redux中将是完全面向未来的,主要用于可追溯性和重放以进行测试。 但是,对于如何正确构造此代码重用(组件,操作和简化程序)仍然存在一些疑问。
在我们的应用程序中,我们以类似连接的方式解决了这个问题。 https://github.com/Babo-Ltd/redux-state
我很少发现在Redux中存储状态很尴尬。 我喜欢它提供的功能,如果需要的话,我可以让它与其他组件交互。
我想到的唯一尴尬之处就是处理表单元素的状态,因为其中可能有很多元素。 在那种情况下,我使用像redux-form这样的库。
我的观点是,除非有尴尬,否则请使用单状态树,并且仅在为您简化事情而不是使事情复杂化时才这样做。 那是唯一的指导
@gaearon您想分享有关如何决定何时或如何将其视为_awkward_的经验吗? 例如,您能否给出一些典型的场景/示例,如果将它们置于应用程序状态原子中,您会觉得很尴尬吗? 提前致谢!
@idavollen我可以给几个:
<input>
更改时,可以使用state来立即更新值(并且您不会冒其他组件不必要地重新呈现的风险)并以去抖动的方式更新商店基本上,您应该将其用于不会影响其他组件的状态。
我要考虑的第三种情况是,例如,我有一个表单(一个在渲染方法中具有昂贵计算量的React容器组件),该表单包含许多行,前面有复选框,还有一个提交按钮。 单击“提交”按钮时,表单应提交其复选框已选中的行,这意味着在用户切换表单时,在提交表单之前各个复选框的瞬态状态并不重要,不应视为组件状态,并且不应导致复选框的切换render()被调用。
暂时,我既不会将复选框的状态置于应用程序状态原子中,也不会置于本地组件状态,即,形式为react组件,以避免在切换复选框时不必要地调用render(),相反,最好使用instance变量来保存选中的复选框,但是它是针对react-pattern的。
我想知道在这种情况下管理复选框状态的最佳方法是什么?
@idavollen那些复选框不受控制吗? 如果不是,则无论如何都要重新渲染它们,因此您可以跟踪在组件状态下选择了哪些组件,并使用它来选择要提交的数据部分。
与组件状态相比,Redux有多快? 在实际事件和渲染过程之间的时间安排上,我应该依靠Redux来获得真正明智的UI道具吗? 我有一个复杂的情况,在这种情况下,使用组件状态将需要很多不那么直接的通信。 在这方面的任何帮助将不胜感激。
@tiberiumaxim以我的经验(我在
由您决定Redux中的状态以及组件的本地状态。 您可能需要阅读http://redux.js.org/docs/faq/OrganizingState.html#organizing -state-only-redux-state和http://redux.js.org/docs/faq/Performance。 html#performance -scaling了解一些相关信息。
@deowk非常感谢您分享有关表演的知识,我也
另外, @ markerikson感谢您提供的链接,我将再次对其进行查看。
不知道是否有人明确提到过,但是Redux也可以管理本地组件状态。 您可以在组件构造函数中创建商店。 此本地存储区将包含此组件的状态,并处理与该组件相关的操作(以及通过传递此存储区的dispatch
函数或调用该dispatch
函数的回调来处理其子级) 。
该架构可以手动实现,也可以使用一些库,例如redux-fractal,redux-ui等。
或者,如Dan所指出的,您甚至还可以实施reducer样式的方法来更新组件的状态! 参见https://twitter.com/dan_abramov/status/736310245945933824
我想知道在哪里存储UI状态,例如活动指示器和模式打开或关闭。 使用setState会在反应组件的单元测试中引起问题吗?
@ akshay2604 :同样,这取决于您。 请参阅我在此线程的先前评论中发布的链接。 在测试组件时,您绝对可以使用setState
,尤其是在使用酶库帮助测试它们时。
让我分享我的想法。
根据状态与应用程序输入/输出的关系,我将状态分为3大类:
{ "todos": [{ id: 1, title: "title" }, { id: 2, title: "title" }] }
{ "changeTodo": { title: "title", action: "add", done: false },
{ id: 1, title: "changed title", action: "modify", done: true }, { id: 2, action: "delete" } }
{ "displayOptions": { searchTerm: "title", sort: ["title", "desc"], filter: "done=false" } }
您不会将cache
与changes
混合,不会将cache
与view
混合,也不会将view
与changes
。
当然,您需要以某种方式合并这些内容,以将其显示在单个react组件中。 这样您的“合并”对象变为:
{ "todos": [{id: undefined, title: "title", done: false }, { id: 1, title: "changed title" }] }
但这不是状态,而是应用程序逻辑的结果。 因此,您不应将其保留在任何地方。
您可以在选择器中缓存此合并状态,可以将其缓存在存储中,也可以将其缓存在组件中,但这不是“状态”,这并不重要,您可以轻松地将应用程序逻辑功能重新
Redux维护人员将告诉您:“嘿,您应该将应用程序逻辑实现为多个reducer和多个事件,这将构成“合并”对象的不同部分。”
我认为这是错误的方法。 这意味着您将无法摆脱redux。 旨在作为一种管理您的状态的工具的原本打算成为应用程序逻辑的实现。
嗨,我深入研究了React / Redux几天,了解了决定选择是否推广某些信息以将其存储或保持在组件本地状态的决定。
但是,如果1个动作同时需要两个动作怎么办? 这是用例,这很常见:
{ modelItems: myModelItems }
。 它缓存服务器模型(的一部分/投影)MyItemsView
,该组件显示存储/缓存项的UI列表。 安装后,组件将触发模型项获取。 组件输入: myModelItems
。 组件输出: fetchModelItems
动作触发。 组件内部: state.busy
和state.errorId
MyItemsView
触发fetchModelItems
,会将HTTP请求发送到服务器,以获取模型并将其缓存在存储区中。 此异步操作分为4个阶段,并包含许多钩子: onStarted
(HTTP请求即将起飞,正在组件中开始加载微调器), onEnded
(已到达HTTP响应,正在组件中停止微调器) ,仍然没有查看响应状态), onFailed
(组件中显示错误通知)和onSucceed
(Redux存储已更新,然后将其持有的模型缓存转发到用于渲染的组件)因此,如何拆分fetchModelItems
动作触发,以便:
onStarted
, onEnded
和onFailed
没有到达Redux商店吗? 这是因为onStarted
和onEnded
是瞬时UI状态,不应与模型缓存交互。 onFailed
(模型缓存保持不变,UI会呈现错误通知)。 MyItemsView
将在此处呈现UI状态,而不是缓存模型onSucceed
上Redux商店了吗? 这是因为它赢得了模型缓存彩票,被授予访问真相源的权限,将进行淋浴和补水,然后在MyItemsView
渲染新的myModelItems
MyItemsView
关于我当前的尝试实现,Redux Thunk负责fetchModelItems
动作触发。 我觉得我在那儿选择了Redux路径,而且我再也无法改变主意了,告诉我“将异步成功传递给Redux存储,将其他任何东西保持在React组件状态”。
对此感到不解的是,我获取成功存储的内容,并将其转发到连接的组件(即Redux容器)。 但是没有加载微调器和可悲的控制台记录的错误消息atm。 我不想定义store.ui.myItemsView.busy
或store.ui.myItemsView.errorId
,而只是在组件状态下获取那些信息。
@pascalav :
这是一个错误跟踪器,而不是支持系统。 对于使用方面的问题,请使用Stack Overflow或Reactiflux,那里有更多人准备帮助您-您可能会更快地得到更好的答案。 谢谢!
最有用的评论
将React用于短暂状态,该状态对应用程序全局无关紧要,并且不会以复杂的方式进行更改。 例如,在某些UI元素中切换,即表单输入状态。 将Redux用于全局重要的状态或以复杂方式突变的状态。 例如,缓存的用户或后期草稿。
有时,您可能希望从Redux状态转换为React状态(当在Redux中存储内容变得笨拙时),或者反过来(当更多组件需要访问某些曾经是本地的状态时)。
经验法则是:做些不太尴尬的事情。