不使用 Redux 的 React,或者我如何学会拥抱 RxJS
每当我开始一个新的 Web 应用时,我几乎都会用到相同的库。React 和 Redux 是其中的佼佼者。
然而,很多时候我发现 Redux Store 只用于 UI 中特定区域的数据,而不是用于更全局的状态。因此,最近我决定尝试一种不同的方法,也就是 Context API 和 RxJS。
现在,我才刚刚开始,但已经感觉它很有潜力了。
我在这里的第一个任务是身份验证。这是一个必须登录才能执行任何操作的应用程序,所以这一点非常重要。为了简化流程,我采用了将电子邮件地址输入与登录/注册表单分开的方法,以便系统能够检测您是否已注册并显示正确的表单。
这意味着我得到了以下 React 组件层次结构:
- 应用程序
- 主页
- 登录注册
- 电子邮件入口
- 登录
- 登记
该EmailEntry
组件显示一个简单的表单,用于请求用户输入电子邮件地址。当用户提交电子邮件地址时,会触发一个操作,在服务器中查找电子邮件地址,然后LoginRegister
组件会根据情况渲染Login
或Register
组件。简而言之,状态转换如下:
- 未定义 => 电子邮件条目
- PENDING => EmailEntry,但有加载指示,表明它正在工作
- 存在 => 登录
- 未知 => 注册
所以,这一切都进入了 Redux 并且一切正常。EmailEntry
组件调度了checkEmailAddress
action。这导致 Redux Saga 触发,结果如下:
- 调度
checkEmailAddress_STARTED
动作 - 进行 API 调用
- 使用
checkEmailAddress_SUCCESS
有效载荷true
或false
- 调度
checkEmailAddress_FINISHED
动作
checkEmailAddress_STARTED
然后为和操作设置 Reducer,以根据需要更新和checkEmailAddress_SUCCESS
的存储值。emailValue
emailStatus
LoginRegister
然后设置组件以对该值做出反应并emailStatus
进行适当的渲染。
这些都是非常简单的 Redux,但代码量也很大。而且几乎所有代码都与这个特定的组件层级结构息息相关。应用程序中的其他组件并不关心我们是否正在检查电子邮件地址、电子邮件地址是什么以及检查状态如何。然而,这些信息都存储在全局存储中,供所有人查看。
所以,我重写了它。我完全删除了 Redux,并改为编写以下内容:
- 一个简单的模块,
checkEmailService
只有一个方法 -checkEmail
。它接受电子邮件地址并返回一个Observable
作为结果。 - 当表单上的表格
EmailEntry
提交后,我们:- 更新本地状态以显示表单正在等待处理
checkEmail
使用输入的地址调用方法- 订阅返回的。当它解析后,我们会使用电子邮件地址和 API 调用的结果
Observable
调用提供的回调函数LoginRegister
- 当
LoginRegister
触发回调时,我们会使用提供的电子邮件地址及其状态更新本地状态 - 然后,组件
LoginRegister
使用此本地状态来确定要渲染哪个组件。
这意味着:
- 挂起标志仅对
EmailEntry
组件而言是本地的 - 电子邮件地址和状态仅对
LoginRegister
组件本地有效 - 根本没有全球国家
这样已经感觉更简洁了。我们摆脱了所有全局状态,这是一个巨大的优势(我们都知道全局变量有多糟糕。为什么全局状态会更好呢?)
但有时我们确实有一些对应用程序更重要的值。例如,当前用户可能很重要,或者经过身份验证的访问令牌。我还没有实现这些,但我考虑了两种方法。
对于实际的全局值,我将使用Subject
- 确切地说BehaviorSubject
是 - 而不是Observable
。服务调用可以在需要时更新它,并且任何对象都可以订阅当前值。访问令牌就是这样一个值 - 它以 开始undefined
,但在身份验证时会被赋予一个值。任何需要当前值的对象都可以通过使用 来获取它Subject
,getValue
或者可以订阅以便在值发生变化时收到通知。
出于以 UI 为中心的考虑,我正在考虑将其与 Context API 耦合,并在组件树的适当位置放置一个组件作为 Provider 并订阅Subject
。每当 发生Subject
更改时,此组件都会更新其本地值并将其传递给 Context API。之后,任何需要它的下级组件都可以从 Context API 访问它,而无需了解生成它的 API 调用。这意味着只有一个 的订阅者Subject
需要执行更新,其余部分由 React 处理。
所有这些似乎给了我大部分的 Redux 功能,而不需要 Redux 本身。
缺少的是编排。事实上,一个被调度的 action 可以触发 store 的多个部分做出响应。这也可以相对简单地通过服务 API 调用其他服务 API 来实现。例如,身份验证的操作如下:
- 将电子邮件和密码发送到服务器,并返回访问令牌和用户 ID
- 存储访问令牌
- 将用户 ID 存储为当前用户 ID
- 调用服务器获取当前用户 ID 的用户详细信息
Redux 允许 store 的不同部分对相同的 action 做出响应,从而实现很多类似的功能。例如:
authenticate_SUCCESS
导致访问令牌减少器存储访问令牌authenticate_SUCCESS
导致当前用户 Reducer 存储用户 IDauthenticate_SUCCESS
导致 Saga 使用getUser
给定的用户 ID 来调度动作getUser_SUCCESS
导致用户详细信息 Reducer 存储用户详细信息
所有这些都由一个动作串联起来。这样可以,但在代码中很难追踪。因此,我计划使用一个authenticationService
which 语句:
- 调用
accessTokenService
以获取访问令牌 - 调用
currentUserService
来存储用户 ID - 调用
getUserService
以获取(并缓存)用户详细信息
这提供了非常易读的编排,并使调试和测试变得非常简单。
这会起作用吗?我还不知道。
它会比 Redux 更好吗?我还不知道。
但我非常想看看事情进展如何。
鏂囩珷鏉ユ簮锛�https://dev.to/grahamcox82/react-without-redux-or-how-i-learnt-to-embrace-rxjs-1n05