不使用 Redux 的 React,或者我如何学会拥抱 RxJS

2025-06-08

不使用 Redux 的 React,或者我如何学会拥抱 RxJS

每当我开始一个新的 Web 应用时,我几乎都会用到相同的库。React 和 Redux 是其中的佼佼者。

然而,很多时候我发现 Redux Store 只用于 UI 中特定区域的数据,而不是用于更全局的状态。因此,最近我决定尝试一种不同的方法,也就是 Context API 和 RxJS。

现在,我才刚刚开始,但已经感觉它很有潜力了。

我在这里的第一个任务是身份验证。这是一个必须登录才能执行任何操作的应用程序,所以这一点非常重要。为了简化流程,我采用了将电子邮件地址输入与登录/注册表单分开的方法,以便系统能够检测您是否已注册并显示正确的表单。

这意味着我得到了以下 React 组件层次结构:

  • 应用程序
    • 主页
    • 登录注册
      • 电子邮件入口
      • 登录
      • 登记

EmailEntry组件显示一个简单的表单,用于请求用户输入电子邮件地址。当用户提交电子邮件地址时,会触发一个操作,在服务器中查找电子邮件地址,然后LoginRegister组件会根据情况渲染LoginRegister组件。简而言之,状态转换如下:

  • 未定义 => 电子邮件条目
  • PENDING => EmailEntry,但有加载指示,表明它正在工作
  • 存在 => 登录
  • 未知 => 注册

所以,这一切都进入了 Redux 并且一切正常。EmailEntry组件调度了checkEmailAddressaction。这导致 Redux Saga 触发,结果如下:

  • 调度checkEmailAddress_STARTED动作
  • 进行 API 调用
  • 使用checkEmailAddress_SUCCESS有效载荷truefalse
  • 调度checkEmailAddress_FINISHED动作

checkEmailAddress_STARTED然后为和操作设置 Reducer,以根据需要更新checkEmailAddress_SUCCESS的存储值emailValueemailStatus

LoginRegister然后设置组件以对该值做出反应emailStatus进行适当的渲染。

这些都是非常简单的 Redux,但代码量也很大。而且几乎所有代码都与这个特定的组件层级结构息息相关。应用程序中的其他组件并不关心我们是否正在检查电子邮件地址、电子邮件地址是什么以及检查状态如何。然而,这些信息都存储在全局存储中,供所有人查看。

所以,我重写了它。我完全删除了 Redux,并改为编写以下内容:

  • 一个简单的模块,checkEmailService只有一个方法 - checkEmail。它接受电子邮件地址并返回一个Observable作为结果。
  • 当表单上的表格EmailEntry提交后,我们:
    • 更新本地状态以显示表单正在等待处理
    • checkEmail使用输入的地址调用方法
    • 订阅返回的。当它解析后,我们会使用电子邮件地址和 API 调用的结果Observable调用提供的回调函数LoginRegister
  • LoginRegister触发回调时,我们会使用提供的电子邮件地址及其状态更新本地状态
  • 然后,组件LoginRegister使用此本地状态来确定要渲染哪个组件。

这意味着:

  • 挂起标志仅对EmailEntry组件而言是本地的
  • 电子邮件地址和状态仅对LoginRegister组件本地有效
  • 根本没有全球国家

这样已经感觉更简洁了。我们摆脱了所有全局状态,这是一个巨大的优势(我们都知道全局变量有多糟糕。为什么全局状态会更好呢?)

但有时我们确实有一些对应用程序更重要的值。例如,当前用户可能很重要,或者经过身份验证的访问令牌。我还没有实现这些,但我考虑了两种方法。

对于实际的全局值,我将使用Subject- 确切地说BehaviorSubject是 - 而不是Observable。服务调用可以在需要时更新它,并且任何对象都可以订阅当前值。访问令牌就是这样一个值 - 它以 开始undefined,但在身份验证时会被赋予一个值。任何需要当前值的对象都可以通过使用 来获取它SubjectgetValue或者可以订阅以便在值发生变化时收到通知。

出于以 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 存储用户 ID
  • authenticate_SUCCESS导致 Saga 使用getUser给定的用户 ID 来调度动作
  • getUser_SUCCESS导致用户详细信息 Reducer 存储用户详细信息

所有这些都由一个动作串联起来。这样可以,但在代码中很难追踪。因此,我计划使用一个authenticationServicewhich 语句:

  • 调用accessTokenService以获取访问令牌
  • 调用currentUserService来存储用户 ID
  • 调用getUserService以获取(并缓存)用户详细信息

这提供了非常易读的编排,并使调试和测试变得非常简单。

这会起作用吗?我还不知道。

它会比 Redux 更好吗?我还不知道。

但我非常想看看事情进展如何。

鏂囩珷鏉ユ簮锛�https://dev.to/grahamcox82/react-without-redux-or-how-i-learnt-to-embrace-rxjs-1n05
PREV
10 个清洁 DEV 文章的技巧!好的,感谢您阅读!😹 🤪 🤣
NEXT
你用 HTML 或 CSS 制作过哪些酷炫的作品?S1:E6 - 鲜为人知的生产力工具;大幅提升生产力