iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
Software Development

From State Machine to XState系列 第 22

Day22 - 在 XState, 狀態機器裡無窮盡的狀態、 資料:Extended State and context and assign API - 2

  • 分享至 

  • xImage
  •  

問題延伸

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


上一篇
Day21 - 在 XState, 狀態機器裡無窮盡的狀態、 資料:Extended State and context - 1
下一篇
Day23 - 在 XState 中的平行式狀態 Parallel States
系列文
From State Machine to XState31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Dylan
iT邦新手 3 級 ‧ 2021-11-01 11:30:26

話說 同理,進入到「關著」的狀態之前,我們會先 下方的 code 好像錯了,附到「開了」的

Ken Chen iT邦新手 4 級 ‧ 2021-11-02 13:43:34 檢舉

ohQQ
謝謝~貼錯了沒注意到
/images/emoticon/emoticon41.gif

我要留言

立即登入留言