State Management

What is State ?

舉例來說:

  1. 使用者登入狀態
  2. 購物車狀態
  3. 訊息狀態

所有可互動的都有狀態

feat. 冠群的 slide

What is State Management ?

先由使用者互動改變狀態

畫面會依據不同的狀態去顯示

管理 資料 和 使用者操作事件 的互動流程

Redux

一個可預測的狀態容器

Flux Pattern Based

單向資料流

將狀態視作一連串的狀態流

每次的事件都是產生一個新的狀態

Before Flux : Only MVC

雙向資料綁定 → 連鎖更新

不容易去預測一個單一互動所造成的改變結果

MVC 只定義了三個角色的功能與關係
有了 FLUX 能更明確的定義「與資料互動的方式」

Single Store : Single Source of Truth

Redux

  1. Action 描述行為
  2. Reducer 通知改變
  3. Store 存值

Xstate

Finite State Machine (FSM) & Statechart based

Why Xstate ?

  1. 區分狀態 ( state ) 和 資料 ( context )
  2. 事先定義狀態(UI不會出現未知的狀態)
  3. XState Visualizer 可展示 StateChart

What is FSM ?

  1. 狀態總數(state)是有限的。
  2. 任一時刻,只處在一種狀態之中。
  3. 某種條件下,會從一種狀態轉變(transition)到另一種狀態。

有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型

例如:紅綠燈

(  FSM 是很嚴謹的模型,這邊只是粗淺介紹 )

In Practice, we need ...

  1. 有限數量的狀態 (state)
  2. 有限數量的事件 (event)
  3. 一個初始狀態 (initial state)
  4. 一個轉換函式 (transition function)
  5. 具有 0 至 n 個最終狀態 (final state)

簡言之 就是 定義 各狀態 ( State )過渡方法 ( Action )

StateChart & Demo

import React from 'react';
import { useMachine } from '@xstate/react';
import { lightMachine } from './lightMachine';

function App() {
  const [state, send] = useMachine(lightMachine);
  return (
    <div className="App">
      {state.matches(LIGHT_STATES.RED) && <RedLight />}
      {state.matches(LIGHT_STATES.GREEN) && <GreenLight />}
      {state.matches(LIGHT_STATES.YELLOW) && <YellowLight />}
      <button
        onClick={() => {
          send(LIGHT_EVENTS.CLICK);
        }}
      >
        click me
      </button>
    </div>
  );
}
import { Machine } from 'xstate';

const LIGHT_STATES = {
  RED: 'RED',
  GREEN: 'GREEN',
  YELLOW: 'YELLOW',
};

const LIGHT_EVENTS = {
  CLICK: 'CLICK',
};

export const lightMachine = Machine({
  initial: LIGHT_STATES.RED,
  states: {
    [LIGHT_STATES.RED]: {
      on: {
        [LIGHT_EVENTS.CLICK]: LIGHT_STATES.GREEN,
      },
    },
    [LIGHT_STATES.GREEN]: {
      on: {
        [LIGHT_EVENTS.CLICK]: LIGHT_STATES.YELLOW,
      },
    },
    [LIGHT_STATES.YELLOW]: {
      on: {
        [LIGHT_EVENTS.CLICK]: LIGHT_STATES.RED,
      },
    },
  },
});
  1. State & Action 統一在 Machine 處理
  2. View 用 useMachine 來取得 service

Effect Handle

What is Effect ?

資料改變 或 使用者觸發事件 後產生的後續影響

Side Effect

運算的過程中,改變了系統狀態或是對外部世界進行交互。

如:非同步請求( Network Request )而產生的資料不一致

Redux

Redux Side Effect Management

本身自己不處理 Side Effect,依賴於 其他套件處理 ( middleware )

Redux thunk

import {
  createStore,
  combineReducers,
  compose,
  applyMiddleware,
} from 'redux';
import thunk from 'redux-thunk';

const store = createStore(combineReducers({
}), {}, compose(
  applyMiddleware(
    thunk,
  ),
));

export default store;

Xstate Effects

  1. Fire-and-forget effects ( synchronously  )
    Real-time 操作,執行動作後,直接改變狀態。
     
  2. Invoked effects ( asynchronously  )
    可發送或接收非同步請求。
  1. entry : upon entering a state
  2. exit : upon exiting a state
  3. transition actions : when a transition is taken
  1. src : Promise handler
  2. resolve : onDone Transition
  3. reject : onError Transition

Redux

Xstate

  1. 生態系完整
  2. 僅需對資料流做控管
  1. State 和 context 定義清楚
  2. 使用情境可預測性高
  3. 可細拆分 machine
  1. 使用上較不直覺
  2. 狀態切換不明確
  1. 事先定義撰寫相對麻煩
  2. 狀態擴充複雜度易太高

One More Thing

Flow Control in FSM

By Routing

  1. " / commodity "
  2. " / commodity / : customerCode "
  3. " / commodity / : customerCode / shopping "
  4. " / commodity / : customerCode / payment "
  5. redirect to "/ commodity"

By Routing

  1. 傳遞資料不便
  2. 流程再多一點網址就會無限拉長

By FSM-Like Design

Shopping

Login

Payment

Pending

Success

idle

type Payload =
  | {
      step: 'idle';
    }
  | PayloadShopping
  | PayloadLogin
  | PayloadPayment
  | PayloadPending
  | PayloadSucess;
interface PayloadShopping {
  step: 'shopping';
  customerInfo: CustomerInfo | undefined;
  items: ShoppingCartItemData[];
}

interface PayloadLogin extends Omit<PayloadShopping, 'step'> {
  step: 'login';
}

interface PayloadPayment extends Omit<PayloadLogin, 'step'> {
  accessToken: string;
  step: 'payment';
}

interface PayloadPending extends Omit<PayloadPayment, 'step'> {
  receivedAmount: number;
  step: 'pending';
}

interface PayloadSucess extends Pick<PayloadPending, 'receivedAmount'> {
  payment: CheckoutCashPaymentMutation_payment;
  step: 'success';
}
{payload.step === 'idle' && (
  <EnterCustomerNoModal
    onNextStep={...}
  />
)}
{payload.step === 'shopping' && (
  <Shopping
    payload={...}
    onGoBack={...}
    onCheckout={...}
  />
 }
 {payload.step === 'login' && (
   <Login
      payload={...}
      onGoBack={...}
      onCheckout={...}
    />
 )}
 {payload.step === 'payment' && (
   <Payment
      payload={...}
      onGoBack={...}
      onPayment={...}
    />
 )}
 {payload.step === 'pending' && (
  <Pending
      payload={...}
      onGoBack={...}
      onSuccess={...}
    />
 )}
 {payload.step === 'success' && (
  <Success
      payload={...}
      onGoBack={...}
      onOk={...}
    />
 )}

Flow Control

  1. 定義 state 和 context,好預測
  2. Component 採用依賴抽離模式
  3. 可讀性高,方便Debug & 測試

THANKS  FOR  LISTENING

Made with Slides.com