iT邦幫忙

0

XState 帶來的狀態管理新視角

  • 分享至 

  • xImage
  •  

設計先行預防出錯

除蟲花掉的時間可長可短,是不可控的因素,它可能佔用了好幾個小時就為了一個錯誤。當應用程式越來越大時,定位錯誤成了問題。有些操作是在某特定狀況下操作才有意義,如果有一個狀態圖成為真相的來源是不是更好?

傳統做法的問題

先來看一下原始代碼:

// viewing -> editing -> viewing 
const [value, setValue] = useState('');
const [committedValue, setCommittedValue] = useState('');
const [mode, setMode] = useState('viewing');

const textEditStart = () => {
  setValue(committedValue);
  setMode('editing');
}

const textChange = (newValue) => {
  setValue(newValue);
}

const textCommit = () => {
  setCommittedValue(value);
  setMode('viewing');
}

const textCancel = () => {
  setValue(committedValue);
  setMode('viewing');
}

傳統做法的潛在問題

  1. 狀態不一致的風險

    // 可能出現的錯誤情況
    setMode('editing');
    // 如果這裡出錯,mode 已經改變但 value 沒有載入
    setValue(committedValue);
    
  2. 無效操作沒有保護

    // viewing 狀態下誤觸 textChange
    textChange('new value'); // 沒有檢查 mode,直接修改了 value
    
    // editing 狀態下誤觸 textEditStart
    textEditStart(); // 可能導致資料丟失
    
  3. 邏輯散落各處

    • 狀態檢查邏輯分散在各個函數中
    • 容易遺漏邊界情況
    • 難以追蹤完整的狀態流程
  4. 除錯困難

    // 出現 bug 時很難回答這些問題:
    // - 在什麼狀態下發生的?
    // - 允許的狀態轉換有哪些?
    // - 是誰觸發了這個狀態變化?
    

XState 狀態機方案

如果我們改成這樣:

const textMachine = {
  context: { value: '', committedValue: '' },
  initial: 'viewing',
  states: {
    viewing: {
      on: {
        EDIT_START: {
          target: 'editing',
          actions: {
            value: ({ context }) => context.committedValue
          }
        }
      }
    },
    editing: {
      on: {
        TEXT_CHANGE: {
          actions: {
            value: (_, event) => event.value
          }
        },
        TEXT_COMMIT: {
          actions: {
            committedValue: ({ context }) => context.value
          },
          target: 'viewing'
        },
        TEXT_CANCEL: {
          actions: {
            value: ({ context }) => context.committedValue
          },
          target: 'viewing'
        }
      }
    }
  }
}

對比分析

層面 傳統做法 XState 狀態機
程式碼量 較少,直觀簡潔 較多,結構化樣板代碼
學習成本 低,React 原生概念 中等,需要理解狀態機概念
錯誤預防 依賴開發者手動檢查 設計層面就避免無效操作
狀態一致性 容易出現不一致 強制保證狀態一致性
邏輯集中性 分散在各個函數 集中在狀態機定義中
可視化 需要閱讀程式碼理解 可直接產生狀態圖
除錯難度 需要追蹤多個變數 明確的狀態和轉換歷史
擴展性 隨複雜度增加而困難 結構化,容易擴展新狀態

核心差異:從操作檢查到情境約束

傳統做法:在操作中檢查

const textChange = (newValue) => {
  // 每次操作都要檢查是否允許
  if (mode !== 'editing') {
    console.warn('只能在編輯模式下修改文字');
    return;
  }
  setValue(newValue);
}

XState:從情境去看操作

editing: {
  on: {
    TEXT_CHANGE: { /* 只有在 editing 狀態才會處理 */ }
    // EDIT_START 事件會被忽略,因為沒有定義
  }
}

實際收益

1. Bug 預防

  • 無效狀態組合:不可能同時處於 viewing 和 editing
  • 無效操作:viewing 狀態下無法觸發 TEXT_CHANGE
  • 狀態一致性:所有相關的狀態變更都是原子操作

2. 開發體驗

  • 可視化:狀態圖成為活的文檔
  • 團隊溝通:產品、設計、開發都能理解狀態圖
  • 測試:明確的狀態和轉換便於撰寫測試

3. 維護性

  • 邏輯集中:所有狀態邏輯都在一個地方
  • 擴展容易:新增狀態和轉換不會影響現有邏輯
  • 重構安全:狀態約束保證重構不會破壞邏輯

權衡考量

何時使用 XState

  • ✅ 複雜的業務流程(多步驟表單、工作流程)
  • ✅ 容易出錯的狀態邏輯(支付流程、連線狀態)
  • ✅ 需要團隊協作的專案
  • ✅ 長期維護的大型應用

何時使用傳統做法

  • ✅ 簡單的 UI 狀態(modal 開關、loading 狀態)
  • ✅ 快速原型開發
  • ✅ 小型專案或個人專案
  • ✅ 團隊對狀態機不熟悉的情況

總結

XState 帶來狀態管理新視角,從情境去看操作而不是在操作中檢查

相比之下,雖然有更多的樣板代碼與 Redux 相似,但唯一差異是這些操作只會在某個情境下觸發,從根本上減少發生 bug 的可能性。

這種**「設計先行」**的思維模式,讓我們在寫程式碼之前就能:

  • 🔍 發現邏輯漏洞
  • 📋 完善需求分析
  • 🚫 排除無效狀態
  • 🔄 設計完整流程

最終實現**「預防勝於治療」**的開發理念,將原本不可控的除錯時間,轉換為可控的設計時間。


「與其花時間除錯,不如花時間設計」- 這就是 XState 狀態機帶給我們的核心價值。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言