Slide 1

Slide 1 text

© LayerX Inc. 状態と共に暮らす:ステートフルへの挑戦 ypresto @ LayerX Frontend Night - Exploring State (2025/04/20)

Slide 2

Slide 2 text

© LayerX Inc. 2 ⾃⼰紹介 ● LayerX バクラク事業部 ○ 債務管理チーム (請求書受取) ○ ソフトウェアエンジニア ○ 経理の⽅が利⽤するプロダクトの開発 ● 趣味は主に写真とスプラ、⼦どもとおでかけ ypresto (プレスト)

Slide 3

Slide 3 text

© LayerX Inc. 3 フロントエンド=ステートフル フロントエンドとはステートフル、だからこそ難しい バックエンド ステートレスに設計し、状態は外部に任せる フロントエンド DOM、フォーム、非同期処理など、ステートフルが本体

Slide 4

Slide 4 text

© LayerX Inc. 4 フロントエンド=ステートフル ● ロジックと状態が容易に結合、分解が困難 ● 順番‧タイミング問題:setTimeout() ● 単体テストが困難、結合テストの肥⼤化 ● コンポーネント内がガチャガチャする...! useEffect()祭り!!! 複雑!!! 状態が中⼼に来ることで‧‧

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

© LayerX Inc. 6 状態という複雑さと共に暮らす ● すべてのアプリケーションには、 それ以上減らせない複雑性がある ● 本質的な複雑さは減らせず、移動しかできない ● 例:UIを簡単にすると、システムのロジックが複雑になる テスラーの複雑性保存則 https://www.nomodes.com/larry-tesler-consulting/complexity-law https://scrapbox.io/koushisa/複雑性保存の法則

Slide 7

Slide 7 text

Apollo 11 (1969) Crew Dragon Endeavour (2020) 参考: https://blog.btrax.com/jp/space-x-touchscreen https://airandspace.si.edu/collection-media/NASM-NASM2013-02663 https://www.youtube.com/live/H-l6f4wcv2I?feature=shared&t=21153

Slide 8

Slide 8 text

© LayerX Inc. 8 状態という複雑さと共に暮らす ● ユーザー=システム要件を減らす? ● システムの中で、 ● 本質的でない複雑さが少なくなる場所 ● 品質担保が容易な場所 ● に移動する! 本質的な複雑さを「どこに移動するか」が⼤事

Slide 9

Slide 9 text

© LayerX Inc. 9 状態という複雑さと共に暮らす ● 状態に対する本質的な操作は2種類! ● 状態という複雑さから引き剥がす ステートフルの複雑性 状態 計算された 状態 (値) 変換ロジック イベントハンドラ リアクティブな接続 ⼿続き的な呼出‧変更 外界 (DOMなど) 状態の 参照‧変更 1. 状態から別の状態を計算 2. イベントに応じて 状態を変更

Slide 10

Slide 10 text

1. 状態から別の状態を計算する

Slide 11

Slide 11 text

© LayerX Inc. 11 リアクティブとDerived State 状態から状態を計算:Derived State

Slide 12

Slide 12 text

12 © LayerX Inc. リアクティブとDerived State:本質的でない複雑さの例 状態が変わるイベントハンドラで、 漏れずに呼ばないといけない

Slide 13

Slide 13 text

© LayerX Inc. 13 リアクティブとDerived State 実際の業務システムで同じこと⾔われたら‧‧

Slide 14

Slide 14 text

© LayerX Inc. 14 リアクティブとDerived State ● イベントハンドラの網羅性やタイミング =本質的でない複雑さを意識しなくて済む ● 複雑な関係性は、純粋関数に移動してテストする ● 外界の複雑さは、(引数扱いの) hooksに移動する ● 状態の組みたてができるのがリアクティブ リアクティブ:状態間の関係を普通のコードで表現 参考: https://zenn.dev/layerx/articles/22dd45dc69a57c

Slide 15

Slide 15 text

© LayerX Inc. 16 リアクティブとDerived State ● 「状態間の関係」の表現に、余計なイベントハンドラが挟まる ○ 更新漏れやタイミングの⼼配、テスタビリティ... ● よって、useState() を不必要に使⽤しない ○ または (状態への参照が制限された) hooksに追い出す ● 余談:フォームのリセットは、Dialogのライフサイクルに委ねたい useEffect() + 常にsetState() は、99%アンチパターン 状態1 状態2 状態1 useEffect(...) 別の状態変更 状態変更

Slide 16

Slide 16 text

Reactivity = Derived State with Confidence. リアクティブで 状態間の関係を簡潔‧安全に定義する

Slide 17

Slide 17 text

© LayerX Inc. 18 状態という複雑さと共に暮らす ● 状態に対する本質的な操作は2種類! ● 状態という複雑さから引き剥がす ステートフルの複雑性 状態 計算された 状態 (値) 変換ロジック イベントハンドラ リアクティブな接続 ⼿続き的な呼出‧変更 外界 (DOMなど) 状態の 参照‧変更 1. 状態から別の状態を計算 2. イベントに応じて 状態を変更

Slide 18

Slide 18 text

2. イベントに応じて状態を変更する

Slide 19

Slide 19 text

onChange={e => setState(e.target.value)}

Slide 20

Slide 20 text

制作‧著作:LayerX 終

Slide 21

Slide 21 text

© LayerX Inc. 22 Event const onChangeUserId = (userId) => { setUserId(userId) setGroupId(users.find(u => u.id = userId)?.groups[0]?.id) # グループを初期値に設定する // 他のフィールドたちの書き換えもここに } 「便利」を実現するほど、複雑化するイベントハンドラ テスタビリティを諦めない..!

Slide 22

Slide 22 text

© LayerX Inc. 23 ● みんな⼤好き (?) Reducer ● nextState = f(state, action) const reducer = action => { switch (action.payload.type) { case "setUser": return { userId: action.payload.userId, groupId: null } ... } ● 「状態の変更」に純粋関数を強制するパターン ○ 複数の (計算されない) 状態を同時に変えたい‧複雑なときに使いましょう イベントハンドラを純粋関数で表現する

Slide 23

Slide 23 text

24 © LayerX Inc. わたしたちのアプリケーションにて。 「旅費交通費」に合わせて 税区分を⾃動設定したい! 「旅費交通費」の デフォルトの税区分設定 外貨のときの 「不課税」税区分設定 適格の経過措置税区分の 対応関係設定 借⽅の勘定科⽬ 請求書の通貨 適格請求書? 借⽅の税区分 get get get set set get しかも税区分変わったら、 ほかも変えたい!!

Slide 24

Slide 24 text

Death. ⼿続き的な 状態操作祭り

Slide 25

Slide 25 text

© LayerX Inc. 26 Event ● フォームへの「変更内容」を計算して返す純粋関数 ○ transform() ● 現在の状態や、1つまえのtransform()が返したdiffを引数で受ける ○ apply(transform(transform(transform({ 借⽅勘定科⽬: 新しい値 }))) ● 設定は、transformを作るときに引数として受ける ● 分解でき、テストでき、いくつでも組み合わせられる ● ASTの変換 (transformer) や、Goのhttp Middewareから着想 ● reducerのような既存パターンに囚われず、アプリケーションの特性にあった設計を! ⽬的は純粋関数で表現することです! どう解決したか

Slide 26

Slide 26 text

Design Functional parts for your application. アプリケーションにあわせて、 ⼩さな純粋関数を組み合わせられるような パターンを発明する

Slide 27

Slide 27 text

© LayerX Inc. 28 ● リアクティブを活⽤し、状態間の関係を意識して、分解して組み⽴てる ● アプリケーションにあった状態変更ロジックの設計をし、⼿続き的な変更操作を減らす ● 純粋関数に切り出せば、「複雑な部分」がテスタブルになる ● 余分な複雑さを減らし、複雑さを移動して、状態と上⼿に付き合っていきましょう..! まとめ

Slide 28

Slide 28 text

© LayerX Inc. 29 フロントエンド=ステートフル ● 純粋関数で書かれた部分はテストしやすい https://www.destroyallsoftware.com/talks/boundaries ● 外界に結合しない部分をぶ厚く中⼼に。そうでない殻の部分を薄く外に。 Functional Core, Imperative Shell