前端客户端中的应用程序状态是一种复杂性,充其量是可以管理的,最糟糕的是您无法交付新功能的原因。在现代前端应用程序中,有很多方法可以处理应用程序状态。您可能熟悉 React 的 useState/useReducer 钩子、Redux 或许多其他状态管理库之一。但是,当您将该状态与业务逻辑配对时,有限状态机可能是一种更好的管理方式。有限状态机是计算的数学模型。它是一个抽象的“机器”,可以在任何给定时间处于有限数量的状态中的一个。机器可以响应某些已知输入从一种状态转换到另一种状态作为事件。您可以通过状态列表、初始状态和触发每个转换的事件来定义有限状态机。让我们举一个真实世界的化妆 (😝) 示例。您正在为您的应用程序构建一个配置文件页面。当用户第一次使用你的应用程序时,他们有一个空白的配置文件。当用户进入个人资料页面时,如果用户有个人资料,您希望个人资料页面显示该个人资料。如果他们没有个人资料,您希望为他们提供一种创建和保存个人资料的方法。如果他们确实有个人资料,您还希望为他们提供编辑和保存个人资料的方式。我们可以在流程图中定义它,而不是将命令式业务逻辑分散在代码中,从开发人员到利益相关者都可以看到和理解。
配置文件加载状态 - 服务器响应配置文件请求时 线条和箭头显示了我们如何从状态转换到状态。有时我们直接转换,有时根据我们转换到一种或另一种状态的条件。现在我们知道所有状态以及如何在它们之间转换,我们可以继续使用 XState 创建我们的状态机。 XState 是最全面的 Javascript 有限状态机库之一,具有可视化等附加工具。我们将首先使用 createMachine 创建一个 XState 机器。我们将给它一个 id 和一个初始状态,我们将称其为正在加载,我们将为 profile 和 error 添加一个空值到您可以想到的上下文中类似于 React 的状态对象。我们也会列出所有可能的状态。我们将添加一个 invoke 属性,该属性指向在 createMachine 调用的第二个参数中定义的 fetchProfile 服务。这意味着当机器处于加载状态时,它会调用 fetchProfile 服务中定义的函数。这是一个 API 调用,它返回一个承诺和一些数据。我们已经在加载状态调用属性上定义了 onDone 和 onError 属性。这些在承诺解决或拒绝时被调用。
如果我们得到一个成功的响应,我们就会触发 XState 的分配操作,用事件数据更新上下文,然后转到 profileLoaded 状态。接下来,让我们将名为 hasProfile 的保护添加到传递给 createMachine 对象的第二个参数中。如果配置文件存在,这将返回一个布尔值。我们还将设置 profileLoaded 状态以检查配置文件是否存在。如果是,则移至 showProfile 状态,如果不是,则移至 noProfile 状态。我们在这里使用“eventless” always 属性来根据守卫自动移动到下一个状态。我们在 noProfile 状态上添加了一个 CREATE_NEW_PROFILE 事件,当触发时,会将状态转换为 createdProfile 状态。我们还在创建配置文件状态上添加了事件来处理保存和取消创建的配置文件。最后,我们将根据我们从前面的步骤中了解到的信息填写其余的状态、转换和服务,最终得到这样的结果:
由于布局的原因,我们的最终状态图可能看起来有点忙,但它与上面的原始流程图具有相同的步骤。此图表示通常定义不明确且分散在整个代码中的业务逻辑。使用 FSM 的优点是我们必须预先考虑这种状态,以便我们可以更好地理解和定义它。仅此一项就可以减少构建过程、维护和附加功能构建期间的错误和沟通不畅。现在要在 React 中使用它,我们只需添加 @xstate/react 包并导入 useMachine 钩子状态是一个具有值(当前机器状态)和上下文(具有我们的配置文件和错误的对象)的对象。状态对象上还有一个匹配函数,可用于匹配状态。我们可以通过发送函数向机器发送状态适当的事件。例如,当处于 showProfile 状态时,我们可以调用 send('EDIT_PROFILE') 并且状态将从 showProfile 转换为正在编辑的配置文件。
当状态对象发生更改时,组件将重新渲染,允许您使用适当的 UI 代码响应这些状态更改。有限状态机是一种非常好的方式,可以迫使您预先考虑应用程序可能处于的状态,然后将这些状态编码到驱动您的用户体验的机器中。我们经常使用多个 useState 或 useReducer 钩子或全局状态管理解决方案来处理 React 中的这种复杂性。这种方法不需要替换它们,但它在您的工具箱中提供了一个有用的工具来控制您可能会遇到的一些更复杂的工作流时不时遇到。