react全局状态管理_React 各状态管理方案详细分析

与我 以前的文章相比,这将是一篇更加具有争议的文章。因此,亲爱的读者,请亲喷——这只是我个人对于React 中的状态管理问题的想法和思考
你为什么应该听听我的看法?
我将 React 应用在大型商业项目中,该项目使用了三种当前最流行的状态管理方法:

  • 只使用 React 内置的状态管理机制,
  • 使用 Redux,
  • 使用 Mobx。


因此,在本文中,我将比较这三个选择。
我的目标是为您提供关于这些状态管理方法相对中立的观点。更进一步,我将给出关于在 React 应用程序中为什么状态管理变得如此重要,以至于引发人们编写无数的库,文章和会议演讲的这个话题的观点。
让我们开始吧!

状态的起源

当我第一次学习前端开发时,没有人谈论“状态管理”。没有人真正关心状态。
在我开发的第一个商业应用程序中,是用不朽的 jQuery 库编写的,人们只是将状态存储在一些随机的地方(某些HTML元素的“data-*”属性),或者根本不把它存放在任何地方。
在第二种情况下,读取状态意味着简单地检查 DOM 中当前呈现的内容。对话窗口是否打开?没有布尔值告诉我们,所以只是检查一下树中是否有一个具有某些特殊类或 id 的 DOM 元素!
当然,这种方法导致了极端混乱和错误的代码库,所以 React 中,应用程序的状态与视图明显分离,对我们来说是一个顿悟,也是应用状态的概念在我们脑海中根深蒂固的时刻。

React状态机制(经典和现代)

自从 React 将状态当成独立的概念提出时,它也提供了一些简单的工具来管理状态。
早先只是一个setState方法,这个方法允许更改我们组件中存储的状态,现在我们又有了一个useState的hook,有一些微小的差异,但最终出于相同的目的——定义和修改每个组件基础的状态。
现在最后一个关键点。在“React”中,每个状态都被定义为在组件“内部”。所以假设FirstComponent将有一个独立于SecondComponent的 state,甚至是每个实例会有FirstComponent自己的 state 实例。(跳出思维定式)这意味着React 组件之间没有状态共享。每个组件都有自己的 state 实例,React 创建了一个管理程序,仅此而已!
但事实却是,我们通常希望在网站的不同位置(即在不同的组件中)显示相同的状态。
例如,应用程序顶部的 Facebook 标题中的新消息数量应当始终等于 messenger 窗口本身底部的未读消息数量。
具有共享状态(消息列表,其中一些被标记为“未读”)来确保两个组件始终显示相同的信息将会轻而易举。Messenger组件将简单地展示列表中的消息,用粗体标记未读的消息。同时Header组件将计算列表上有多少消息被标记为未读,并向用户显示该数字。
一种方案是,有两个独立的 state 副本——一个在Header组件和一个Messenger组件——有可能会导致这些状态不同步。例如,用户可能会看到在Header中有2条未读,但随后他在Messenger中却找不到任何未读消息。那肯定会很恼火。
那么,我们如何只使用React而不使用任何额外的库就实现状态共享呢?
共享状态的规范方法是将其存储在组件树中的共同父节点组件中。然后你可以简单地把这个 state 作为 props 传递下去。这样你就可以通过 props 将相同的状态传递给两个单独的组件。砰!这两个组件现在共享这个 state。
一开始效果很好。但是如果你用这种方式编写你的应用程序(如果它们变得足够复杂),随着时间的转移,你会很快注意到你的很多 state 会“冒泡”。
随着越来越多的组件需要访问相同的state,你进入状态需要放到组件树中越来越高的位置,直到最终到达最顶层的组件。
所以你最终会得到一个巨大的“容器”组件,它基本上存储了你所有的state。它被几十种方法来操纵,并通过几十个props来将其传递到几十个组件中。
这很快变得难以控制。实际上,没有简单的方法可以将代码分割成更小的部分。最后你会得到一个庞大的组件文件,它通常是数千行代码。
你最终会遇到和你之前一样的混乱,你用 React 把 state 从 view 中分开。哎呀……

Redux 救援

发明 Redux 的原因与上文所述有所不同。实际上,它纯粹是一种演示工具,旨在提供开发React应用程序时的“时间旅行”的能力。
事实证明,如果将所有状态都放在一个位置(称为“store”),并且总是一步一步地更新所有状态(使用“reducer”函数),则基本上可以“时间旅行” 。由于您可以序列化保存在存储中的state并在每次更新后保存,因此您可以保留所有过去state的历史记录。
然后,您可以简单地根据命令返回到任何过去的state,将其再次加载回store。你现在是在“时间旅行”——可以看到你的应用的历史记录。
“时间旅行”被认为是一种有利于开发和调试React应用程序的方法。这听起来很棒,人们立刻蜂拥而至。
但事实证明,这种能力并不像人们最初认为的那样有用。实际上,我相信大多数当前现有的Redux应用程序都不会特别的去使用时间旅行,即使是出于调试目的。对于值得使用的场景,这简直太过麻烦了(我仍然更加依赖console.log来调试)。
然而,我认为从一开始 Redux 就应该成为编写复杂 React 应用程序的主要工具。
Redux 中的 state不是在任何基础组件里创建的,相反,是存储在一个中央的内存数据库中(前面提到的store)
因此,潜在的任何组件都可以访问这个state,而无需通过props传递它,这太麻烦了。在Redux中,任何组件都可以直接访问这个store,只需使用特殊的工具函数即可。
这意味着只需很少的努力,你保存在 store 中的任何数据都可以在应用程序的任何位置显示。
由于多个组件可以同时访问状态而没有任何问题,因此状态共享也不再是一个问题。
我们的 Facebook 网站现在可以在我们想要的任何地方显示未读消息的数量,由保留在 store 中的消息列表提供。
将所有的state保存在一个地方听起来有点类似于我们将state保存在一个单独的组件中。但事实证明,由于 Redux 存储更新是由 reducer 函数完成的,而函数非常容易组合,将我们的 Redux 代码库划分为多个文件,按域或责任划分也比管理一个大型“容器”组件容易。
所以 Redux 听起来真的像是我们之前描述的所有问题的解决方案。看起来在React中的状态管理问题已经解决了,现在我们可以继续讨论更有趣的问题。
然而,就像生活一样,事实并不是那么简单。
还有两个我们还没有描述清楚的Redux问题。
尽管组件可以直接读取 Redux store,但它们不能直接更新store。必须用“actions”来请求store自己更新。
最重要的是,Redux 被认为是一种同步机制,因此为了执行任何初始化任务(例如针对该问题的HTTP请求),您需要使用“中间件”来授予异步操作Redux的能力。
所有这些s tore,reducers,actions,middleware(以及一大堆额外的样板代码)使 Redux代码极其冗长。
在Redux中更改一个简单的功能通常会导致修改多个文件。对于新手来说,跟踪 Redux应用程序中正在发生的事情非常困难。一开始看起来很简单的事情——把所有的状态存储在一个地方——很快变成了极其复杂的架构,有人实际上需要几周的时间来适应。
人们意识到。Redux成功后,出现了大量的各种的状态管理库。
这些状态管理库大多数都有一个共同点——他们试图做和 Redux 完全一样的事情,但是用较少的样板代码。
Mobx 成为最火的其中之一。

Mobx的魔法

与 Redux 对函数式编程的关注相反,Mobx决定拥抱老式的面向对象编程(OOP) 理念。
它保留了 Redux 的 store的概念,但是是一个具有某些属性的类。它保留了Redux的action的概念,但是只是简单的方法。
不再有 reducers,因为您可以像在常规类实例中一样更新对象属性。不再有中间件,因为 Mobx 中的方法可以是同步和异步,这使得机制更加灵活。
有趣的是,理念保持不变,但实现方式却大不相同。至少在乍看之下,它形成了一个比Redux更轻巧的框架。
最重要的是,Mobx用的是普通软件开发人员所熟悉的语言。几十年来,面向对象程序设计是典型的程序员培训的一部分,因此,绝大多数使用 React 的程序员都非常熟悉根据类,对象,方法和属性来管理状态。
再一次我们似乎已经解决了问题——我们现在有了一个状态管理库,该库保留了Redux的思想和优点,同时又不那么冗长,并且对新手来说不那么陌生。
那么问题出在哪里呢?事实证明,尽管 Redux 复杂且冗长,但 Mobx 掩饰了其复杂性,伪装成大多数开发人员熟悉的编程模型。
事实证明,与传统的OOP相比,Mobx与 Rx.js 甚至 Excel 有更多的共同点。Mobx 看起来像面向对象的编程,而实际上,它的核心机制是基于完全不同的理念,与 Redux 提倡的函数式编程相比,它对常规编程器的影响更大。
Mobx不是 OOP 库。它是一个反应式编程库,被隐藏在类,对象和方法的语法下。
问题是,当您使用 Mobx 对象并修改其属性时,Mobx 必须以某种方式通知 React 状态已发生更改。为了实现这一点,Mobx 具有一种受反应式编程概念启发的机制。当对该属性进行更改时,Mobx 会“通知”正在使用该属性的所有组件,并且作为响应,这些组件现在可以重新渲染。
到目前为止,可以简单并且完美地工作,这是 Mobx 可以用很少的样板代码就可以实现 Redux 的大部分功能的原因之一。
但是 Mobx 的反应性并不止于此。
一些状态值取决于其他状态值。例如,许多未读消息直接取决于消息列表。当新消息出现在列表中时,未读消息的数量应随之增加。
因此,在Mobx中,当属性更改时,库机制不仅会通知显示该属性的React组件,而且还会通知依赖于该属性的其他属性。
它的工作原理就像Excel,在那里你改变一个单元格的值之后,依赖于该值的单元格也会立即更新。
此外,其中某些属性是以异步方式计算的。例如,如果您的媒体资源是文章ID,则可能要从后端获取该文章的标题和作者。这是两个新属性——title和author——直接取决于先前的属性(文章 ID) 。但是它们不能以同步方式计算。我们需要发出一个异步HTTP请求,等待响应,处理可能发生的任何错误,然后我们才能更新title和author属性。
当您开始深入挖掘时,您会发现 Mobx 具有处理这些情况的大量的机制和工具,并且 Mobx 文档明确鼓励这种编程风格。您开始意识到 Mobx 只是表面上看起来是 OOP,而实际上是完全不同的理念。
而且,事实证明,在足够大的应用程序中,这种特性及其依赖关系图很快变得异常复杂。
如果您曾经看到过一个庞大而复杂的大型Excel文件,以至于每个人都害怕对其进行任何更改——您基本上已经看到了Mobx应用程序。
但是,Mobx 反应性机制对开发人员来说是不可见的。如前所述,它是在类,方法和装饰器的OOP语法下隐藏的。
因此,从程序员的角度来看,Mobx 所做的很多事情都是“神奇的”。我花了许多时间挠头,试图弄清在某些情况下 Mobx 的机制为什么会(或不会)进行一些更新。有些时候我的代码神秘地发送了多个HTTP请求而不是一个。也有一些时候我的代码没有发送任何请求,即使我可以发誓应该需要发送。
当然最后,错误总是在我这边。Mobx 完全可以正常工作。
尽管 Redux 很复杂,因为它基本上将所有内容都交给了你,并要求你进行管理,但 Mobx 却恰恰相反,它通过隐藏复杂之处,并假装它只是一个“常规”的 OOP库。
一种方法导致代码充满样板,多个文件,并且难以跟踪代码不同部分之间的关系。
第二种方法使代码看起来苗条而优雅,但是有时它会执行您不希望且难以分析的事情,因为您实际上不理解库的底层功能。

状态管理的谎言

有趣的是,整篇文章的前提是共享状态是许多现代Web应用程序的共同要求。
但是...真的吗?
我的意思是,当然,有时您必须在应用程序中两个完全不同的位置显示许多未读消息。
但这是否足以需要创建复杂的状态管理解决方案?
也许……也许我们真正需要的只是一种以可管理的方式在组件之间共享状态的方法?
我正在想象有一个useSharedState钩子,它可以像常规的 React 状态钩子一样工作,但是将允许组件访问相同的状态实例,例如通过共享预定义的键:

const [count, setCount] = useSharedState(0, "UNREAD_MESSAGES_COUNT");


实际上,这个想法一点也不陌生。我至少看到了类似于此钩子的几种实现。
似乎人们(有意识或无意识地)都需要这种解决方案。
当然,它还不能解决所有问题。最大的问题是,异步代码(尤其是数据获取)在 React 中仍然比较难处理,并且以 react hooks 语法实现它几乎就像是黑客(事实上,我可能会写一篇有关该确切问题的后续文章)。
但是我仍然会保留我有争议的主张,我在本文开始时就向您保证:
所有与状态管理有关的辩论、创建的库和编写的文章,全部都是因为——React中没有简单的方法可以在组件之间共享状态实例。
现在要记住——我从来没有机会使用这个假想的useSharedStatehook编写完整的商业应用程序。正如我提到的那样,要使这样的应用程序真正易于开发和维护还需要做一些事情。
因此,我现在所说的一切都可能完全被误导,但无论如何我都会说:我们在React中过度设计了状态管理。
在 React 中使用状态已经接近一个完美的解决方案——从视图中分离状态是一个巨大的垫脚石——我们仅缺少一些针对非常具体问题的小的解决方案,例如共享状态或异步获取数据。
我们不需要状态管理框架及库,只需要对核心反应机制进行一些调整(或者只是外部库中的一些小的工具)。
编写大型网络应用程序总是很复杂。状态管理很难。事实上,你的应用程序越大,难度将是指数级的增长。
但是我相信,学习,调试和驯服状态管理库所花费的时间和努力可以转而用于重构您的应用程序,更仔细地构建它,更好地组织代码。
这将导致整个团队更简单,更容易理解和更容易管理的代码。
我看到这是一个React社区已经在慢慢做的转变,越来越多人觉得用 Redux 或 Mobx 编程让人觉得沮丧。

因此,今天我用什么?

当然,Redux 和 Mobx 仍然有他们的位置。它们是真正伟大的状态管理库。解决了非常具体的问题,并带来了特定的优点(同时也带来了特定的缺点)。
如果您想用时间旅行调试,或者需要将可序列化状态存储在一个地方(例如,将其保存在此处或本地存储中),那么Redux是适合你的。
如果您的应用程序状态是高度互连的,并且您希望确保一个属性的更新将导致其他属性的立即更新,那么Mobx模型将非常适合这个场景。
如果你没有任何具体的要求,就从普通的React开始。
我在那篇文章中描述了一些“普通React”方法的问题,但是在实践中自己遇到这些问题是另外一回事。有了这种经验,您将更明智地决定选择哪种状态管理解决方案。
或不选择。;)
感谢阅读!

原文链接:https://dev.to/mpodlasin/my-thoughts-on-endless-battle-of-react-state-management-libraries-setstate-usestate-vs-redux-vs-mobx-2kal

24622f9996c102de22c7e6ebedaa1880.png

 相关推荐

关于 JavaScript 错误处理的最完整指南(下半部)

关于 JavaScript 错误处理的最完整指南(上半部)心的问题

JavaScript 启动性能瓶颈分析与解决方案

从零看清Node源码createServer和负载均衡整个过程

【项目实战】sass使用进阶篇(下)

【项目实战】sass使用基础篇(上)

最详细的从零开始配置 TypeScript 项目的教程

5 款非常好用的开源 Docker 工具

WebSocket 全面知识补全

7个处理JavaScript值为undefined的技巧

immutablejs 是如何优化我们的代码的?

Chrome 新功能尝鲜!— CSS Overview

又一个布局利器, CSS 伪类 :placeholder-shown

封装一个vue视频播放器组件

对于组件的可重用性,大佬给出来6个建议

学习 TS 不要错过的八个工具

Node 中的全链路式日志标记及处理

使用 Node 开发服务器项目时如何高效地打日志?

用TypeScript学设计模式(享元模式)

用TypeScript学设计模式(模板方法模式)

TypeScript 设计模式之适配器模式

用TypeScript学设计模式(观察者模式)

用TypeScript学设计模式(单例模式)

89fa87ce7edc02e91281a42dd5877f08.png

1ac18447332ef2373dcc5aec1335da9b.png点在看的人特别帅/美 77ef4989a98c51cf89718c9b355684bb.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值