Redux - 让不可能的状态成为可能

Redux - Making Impossible States Impossible

TL;DR

是否有库可以在 JavaScript 中使用 ReduxImmutableJS 实现类似 Elm-Union Types 的东西?

想法

嗨!

我对 Redux 比较陌生,仍在探索最佳使用模式。我花了一些时间学习 Elm,在 Elm 中,整个应用程序的核心焦点是 Model

Elm 的核心原则是制作无错误 应用程序。与 JavaScript 相比,Elm 包含基本类型 (String, List, Boolean...) 因为它也是一种静态类型语言。另一方面,您可以使用 ImmutableJSFlow 并在 JavaScript 中获得相当数量的完整类型和类型检查,这使得这非常均匀。

但是 ElmJavaScript 之间的对比带有一个称为 Union Types 的特征。使用 Union Types 你可以这样说:

type alias User = {
  name: String
}
type Session = LoggedOut | LoggedIn User

这意味着我们在会话方面有两种选择:

这在处理 "HOC" 时非常有用,我们可以简单地说:

case session of 
  LoggedOut ->
    displayLoginButton
  LoggedIn user ->
    displayUser user

此外,您可以在获取数据时使用相同的模式。当数据正在获取时,状态为 Fetching 一旦获取,状态为 Fetched Data,如果发生错误,状态为 FetchingError Error

这使得数据非常安全和干净。写在 JavaScript/Redux 中的相同问题通常会像这样解决:

const model = {
  loading: false,
  loaded: false,
  error: null,
  data: null
}

起初这似乎很正常,但如您所见,可能会遇到 "impossible" 情况。例如:

const model = {
  loading: true,
  loaded: true,
  error: null,
  data: null
}

无法同时加载数据!

问题

有没有办法实现与 JavaScript-Redux 类似的概念,并且仍然保持 ImmutableJS 的不变性? 或者,如果有一种方法可以重新排列模型,使 不可能的状态在 Redux 中变得不可能

P.S.

在 Elm 中谈论这个很棒:"Making Impossible States Impossible" by Richard Feldman

你可以这样破解它:

const Session = {
  LOGGED_IN: user => ({ type: 'LOGGED_IN', user }),
  LOGGED_OUT: () => ({ type: 'LOGGED_OUT' })
}

switch (session.type) {
  case 'LOGGED_IN': {
    viewUser(session.user)
  }

  case 'LOGGED_OUT': {
    viewLoginButton()
  }
}

不,React 或 Redux 中没有专门内置的东西可以实现这一点。这种事情更多是在语言层面。您可能需要查看 TypeScript 或 Flow 以添加对此有帮助的静态类型。除此之外,您可能可以使用显式状态机方法或仔细的 reducer 逻辑检查将某些东西放在一起。

我列表中的 React/Redux links list has a section on static typing tools that may be useful in getting started. In addition, since you mentioned the "loading states" example, the React and Ajax 类别专门链接到几篇文章,这些文章讨论了在 TS、Flow 和其他几个解决方案中处理该概念的方法。