Day22 - 在 XState, 状态机器里无穷尽的状态、 资料:Extended State and context and assign API - 2

问题延伸

1. 请问如何动态载入不同的 context ?

比如说我们这台状态机想要给许多不同的店家共用,比如「绿洲酒吧」、「苹果旗舰店」...

难道我需要一直写一堆 appleStoreMachineConfig, oasisBarMachineConfig 却只有 context 些微不一样吗?

答案是「不用的」

  1. 程序面 的实作,可以透过 factory function ,给定特定的参数,制造出不同的 config
const createDoorMachine = (shopName) => {
  return createMachine({
    id: 'door',
    // shopName 来自 function 参数
    context: {
      shopName
    }
    // ...
  });
};

const appleStoreDoorMachine = createDoorMachine('苹果旗舰店');
const oasisBarDoorMachine = createDoorMachine('绿洲酒吧');

  1. 假设今天 Machine 已经被建立出来了,我们有办法以外挂的方式,加载进 现存 的 machine 吗? 没问题!

XState 的 machine 除了 someMachine.transition(state, event) 之外,也有提供方法 someMachine.withContext(...) 让我们能得到一台新的被动态加载 context 的状态机里。

const someContext = { shopName: "苹果旗舰店" };
const dynamicDoorMachine = doorMachine.withContext(someContext);

const service = interpret(dynamicDoorMachine).onTransition((state) => {
  console.log(`State is ${state.value}`);
});

2. 请问我有办法修改 context 的值吗?

比如说今天我们「苹果旗舰店」想要改店名成「苹果尊绝不凡旗舰店」,我可以直接在状态机里面修改 context value 吗?

可以的! XState 官方提供给我们另外一个 API assign (指派),让我们可以在 action 时,指定、修改 context 。

TLDR: codeSandbox DEMO


前情提要:Action 被触发的三种时机

  1. 离开状态时(onExit)
  2. 状态转移中(onEntry)
  3. 进入状态时(onTransition)

让我们来看看如何修改店名,假设我们今天进开门时会看到 「苹果尊绝不凡旗舰店」关门时会看到「苹果旗舰店」的 LED 灯闪烁。

先来看看 assign 怎麽用!~ assign 是个 higher order function ,它会被先被定义 Action 发生时该怎麽更新 context,然後当实际 Action 发生时,就会去执行 assign 内被定义的修改方式!

assign 吃一个 object 参数,key 是想要被更新的 context value 的 key,以本例而言,就是我们的 shopName 而这个 object 的 value 会是什麽呢? 就是定义如何更新 context 的 function,这个 function 运算回传的结果,就是更新後的 context 。


更新店名为 「苹果旗舰店」

    assign({
            shopName: (context, event) => "苹果旗舰店"
          }),

更新店名为 「苹果尊绝不凡旗舰店」

    assign({
            shopName: (context, event) => "苹果尊绝不凡旗舰店"
          }),

所以这个 assign 会被放在哪里呢?它可以被放在前情提要的三个地方


首先,先增加 Side Effect "LED 显示" 来展示我们状态机 当前的 cotext - shopName

   {
     actions: {
       拉开大门: () => console.error("side effect..........拉开厚重的门...."),
       关上大门: () => console.error("side effect..........推回厚重的门...."),
+      "LED 显示": (context, event) => console.log(`${event.type}!~ LED 显示 『${context.shopName}』`),
     }
   }

需求

开门时会看到 「苹果尊绝不凡旗舰店」关门时会看到「苹果旗舰店」的 LED 灯闪烁。

想必 我们应该是需要在 entry 进去 state 之前,先修改 context ,然後执行 "LED 显示" 的 side effect

因此我们会将这个需求定义在 entry action 里,前面提及 Action 可以有复数个...

我们在此新增定义进我们的 machineConfig,如此,进入到「开了」的状态之前,我们会先

  1. 把 context 更新为 "苹果尊绝不凡旗舰店"
  2. 执行 side effect "LED 显示" 来跟我们的客户打招呼
      开了: {
        entry: [
          assign({
            shopName: (context, event) => "苹果尊绝不凡旗舰店"
          }),
          "LED 显示"
        ],
        on: {
          关门: { target: "关着", actions: ["关上大门"] }
        }
      }

https://ithelp.ithome.com.tw/upload/images/20211007/20130721xidpqx5BGa.png

同理,进入到「关着」的状态之前,我们会先

  1. 把 context 更新为 "苹果旗舰店"
  2. 执行 side effect "LED 显示" 来跟我们的客户打招呼
      关着: {
        entry: [
          assign({
            shopName: (context) => "苹果旗舰店"
          }),
          "LED 显示"
        ],
        on: {
          开门: { target: "开了", actions: ["拉开大门"] }
        }
      },

https://ithelp.ithome.com.tw/upload/images/20211007/20130721EYTNynq2ed.png

明天~我将会努力举更贴近现实一点的例子 >_<

大家一起加油~~~!

参考资料

https://xstate.js.org/docs/guides/context.html
https://xstate.js.org/docs/guides/action.html


<<:  DAY22:优化器(中)

>>:  Day22 DB-关联式资料库与NoSQL

Angular Reactive Form 表单

表单 表单在实作上是件常见的的处理,为什麽会写这篇呢?实在是因为我太容易忘了 XD 还记得一开始接触...

纯手工打造UART版资料清洗工具之 PySide2 GUI 大补帖 - Part B

从七月开始从零开始研究PySide2 GUI相关设计及程序如何撰写,已经有一段时间了,笔者深深有感,...

[Day 18] 来看看v-pre、v-cloak、v-once呗

嗨嗨大家今天过得好吗?我今天成功煎了一颗蛋。今天的篇幅虽然看似有点短,但是里面的内容却是崭新且重要的...

8.unity角色移动(刚体Rigidbody2D)

刚体2D(Rigidbody 2D) 当物体运动速度远小於光速可被视为理想刚体,可以忽略型变,也就是...

Day 29 - State Monad IV

Review 由於 State 原本可以一篇写完的,被我拖成四篇的关系,所以来回顾一下,哈哈哈哈哈哈...