Slide 1

Slide 1 text

© 2012-2021 BASE, Inc. 1 カートのデータレイヤーに 秩序をもたらすステートマシン 「XState」について BASE Tech Talk #1 〜Next.jsを使ったカート大規模リプレイスPJの裏側〜 @rry

Slide 2

Slide 2 text

© 2012-2021 BASE, Inc. 2 自己紹介 rry(りりぃ) ● 2021年4月に BASE 株式会社へ入社 ○ 2021年4月〜9月くらいまで今回の PJ を担当 ● アクセシビリティやっていきをしています ○ https://devblog.thebase.in/entry/2021/12/ 06/111517 ● メンターは先ほど発表してくれた Ayako さん ● アイコンはスイカじゃなくてコザクラインコ :@ryamakuchi

Slide 3

Slide 3 text

© 2012-2021 BASE, Inc. 3 XState とは?

Slide 4

Slide 4 text

© 2012-2021 BASE, Inc. 4 https://xstate.js.org/docs/ https://github.com/statelyai/xstate XState とは? JS / TS で使えるステートマシン (State Machine・State Chart)のライブラリ

Slide 5

Slide 5 text

© 2012-2021 BASE, Inc. 5 XState とは? 状態遷移図を見ることができる

Slide 6

Slide 6 text

© 2012-2021 BASE, Inc. 6 XState とは? React 専用というわけではなく Vue や Svelte などからも利用できる

Slide 7

Slide 7 text

© 2012-2021 BASE, Inc. 7 カートの状態遷移

Slide 8

Slide 8 text

© 2012-2021 BASE, Inc. 8

Slide 9

Slide 9 text

© 2012-2021 BASE, Inc. 9 ← すごく簡略化したカートの状態遷移図 ・initializing(初期状態) ・empty(カートが空) ・checkout(カートの編集・確認・更新)  ・editing(編集中)  ・finalizing(注文完了後の後始末中)  ・completed(注文および後始末完了) ・orderCompleted(注文完了を表示) 簡略化してもこれだけある 状態の遷移

Slide 10

Slide 10 text

© 2012-2021 BASE, Inc. 10 状態と密接に結びつくデータ それぞれの状態に密接に結びついたデータ(Context)を保持している https://xstate.js.org/docs/guides/context.html Context は状態遷移するときのイベントで変化したりする

Slide 11

Slide 11 text

© 2012-2021 BASE, Inc. 11 状態の遷移に伴う副作用 状態の遷移に伴う副作用を宣言的に記述することができる https://xstate.js.org/docs/guides/effects.html 同期的な副作用も、非同期的な副作用もどちらも扱える

Slide 12

Slide 12 text

© 2012-2021 BASE, Inc. 12 XState を利用すると

Slide 13

Slide 13 text

© 2012-2021 BASE, Inc. 13 XState を利用した簡易的なカートのデモを用意 https://github.com/ryamakuchi/xstate-cart-demo/ https://xstate-cart-demo.vercel.app/

Slide 14

Slide 14 text

© 2012-2021 BASE, Inc. 14 状態の遷移のコード const cartMachine = createMachine( { ... states: { initializing: { on: { LOAD_CHECKOUT: { target: 'checkout', actions: ['loadCheckout'], cond: 'checkoutValid', }, }, }, checkout: { initial: 'editing', states: { editing: { on: { MUTATE_CHECKOUT: [ { actions: ['mutateCheckout'], }, ], ... ・initializing という状態から、LOAD_CHECKOUT という  Action が発火して checkout に遷移 ・checkout の初期状態は checkout.editing なので  checkout.editing に遷移 … といったように、宣言的に状態遷移を書くことができる。 https://xstate.js.org/docs/guides/states.html

Slide 15

Slide 15 text

© 2012-2021 BASE, Inc. 15 状態と密接に結びつくデータのコード export const loadCheckout = assign( (context, event) => { if (event.type !== 'LOAD_CHECKOUT') return context; const { customer, cart, checkout } = event; return { ...context, customer, cart, checkout, }; } ); ・データ(Context)を更新したい場合は、Assign Action   を利用する ・例えば loadCheckout 関数では、event の payload で  渡ってきたデータを return して Context を更新している https://xstate.js.org/docs/guides/context.html#assi gn-action

Slide 16

Slide 16 text

© 2012-2021 BASE, Inc. 16 状態の遷移に伴う副作用のコード finalizing: { invoke: { id: 'finalizeCheckout', src: 'finalizeCheckout', onDone: { target: 'xxx', }, onError: 'yyy' }, }, … services: { finalizeCheckout: async () => { // 後始末的なコード }, }, ・Action を使うと同期的な副作用を及ぼすことが  できるが、非同期的な副作用を及ぼしたいときは  Invokeing Services を利用することができる ・例えば Invoking Promises を利用すると、services で  定義したコードが resolve したら onDone が発火、  reject したら onError が発火する https://xstate.js.org/docs/guides/communication.html

Slide 17

Slide 17 text

© 2012-2021 BASE, Inc. 17 実際に使ってみて

Slide 18

Slide 18 text

© 2012-2021 BASE, Inc. 18 実際に使ってみて 思ったよりも多機能だった あれもこれもだいたいできる いくつか便利と思った機能をご紹介

Slide 19

Slide 19 text

© 2012-2021 BASE, Inc. 19 ・複雑な状態遷移に対応するために、  State Nodes を利用して細かく状態遷移を  定義することができる ・簡易カートの例では checkout State が  editing, finalizing, completed という  State Nodes を持っている https://xstate.js.org/docs/guides/state nodes.html State Nodes 状態(State)はさらに下位の状態(State Nodes)を持つことができる checkout: { initial: 'editing', states: { editing: { on: { FINALIZE_CHECKOUT: 'finalizing', }, }, finalizing: { invoke: { id: 'finalizeCheckout', onDone: 'completed' onError: 'completed', }, }, }, },

Slide 20

Slide 20 text

© 2012-2021 BASE, Inc. 20 Invoking Services ・「状態の遷移に伴う副作用のコード」でも  紹介したが、Invoking Services が便利 ・簡易カートの例では後始末的なコードとして  適当なコードを書いているが、実際のカート  では主に作成・更新系の API を叩く処理  などでたくさん使っている https://xstate.js.org/docs/guides/comm unication.html 状態遷移時に非同期処理などを実行できる finalizing: { invoke: { id: 'finalizeCheckout', src: 'finalizeCheckout', onDone: { target: 'completed', }, onError: 'completed' }, }, … services: { finalizeCheckout: async () => { // 後始末的なコード }, },

Slide 21

Slide 21 text

© 2012-2021 BASE, Inc. 21 ・A の場合は A Action を実行したいが、  B の場合は B Action を実行したい、  みたいなときに便利 ・他にも簡易カートの例のように、  バリデーションが通ったら次の状態に  遷移したい、というような使い方もできる https://xstate.js.org/docs/guides/guard s.html#guards-condition-functions Guards(Condition Functions) initializing: { meta: { label: 'cart に関する処理を開始', }, on: { LOAD_CHECKOUT: { target: 'checkout', actions: ['loadCheckout'], cond: 'checkoutValid', }, }, }, … export const checkoutValid = (_: CartMachineContext, event?: CartMachineEvent) => { if (event?.type === 'LOAD_CHECKOUT') { return ( isCheckout(event.checkout) && isCart(event.cart) && isCustomer(event.customer) ); } return false; }; cond プロパティを使うことで条件による状態遷移ができる

Slide 22

Slide 22 text

© 2012-2021 BASE, Inc. 22 まとめ

Slide 23

Slide 23 text

© 2012-2021 BASE, Inc. 23 まとめ カートの状態とその遷移を柔軟に管理すること ができる 状態に紐付くデータを管理できるため、データ レイヤーに秩序が生まれる 本来やりたい開発(ユーザーへ価値を届ける実 装)にリソースを集中できる 1 2 3

Slide 24

Slide 24 text

© 2012-2021 BASE, Inc. 24 ご静聴 ありがとうございました!