複雑なフォームの jotai 設計 / Designing jotai(state) for Complex Forms #layerx_frontend
by
izumin5210
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
複雑なフォームの jotai 設計 2025-04-23 Exploring State - LayerX Web Frontend Night @izumin5210
Slide 2
Slide 2 text
@izumin5210 © LayerX Inc. whoami LayerX バクラク事業部 (2022-09 -) Platform Engineering 部 Enabling チーム Staff Software Engineer Backend と Web Frontend などを書く Wantedly, Inc. (2018-04 - 2022-08) ISUCON14 4位 「状態管理ライブラリまだいらなくね?」 って言いがち
Slide 3
Slide 3 text
これまでのあらすじ © LayerX Inc. https://speakerdeck.com/izumin5210/number-newt-techtalk-vol-15 3
Slide 4
Slide 4 text
これまでのあらすじ © LayerX Inc. https://speakerdeck.com/izumin5210/number-newt-techtalk-vol-15?slide=24 4
Slide 5
Slide 5 text
これまでのあらすじ © LayerX Inc. https://speakerdeck.com/izumin5210/number-newt-techtalk-vol-15?slide=28 5
Slide 6
Slide 6 text
jotai/状態の設計について、具体例とともに深ぼっていきます © LayerX Inc. 6
Slide 7
Slide 7 text
例. 外貨対応の金額入力 React Hook Form + Zod の実装例 © LayerX Inc. 通貨指定して金額入力できる 日本円換算の金額で設定された 上限バリデーションがある 7
Slide 8
Slide 8 text
jotai 設計: なるべく状態ではなく値に jotai での実装例 © LayerX Inc. 例. 外貨対応の金額入力 amountJpy は状態(State)ではなく、 他の値・状態から決まる値(Derived Value) 状態(State)は減らしたい 状態は書き込み可能 変な値が書き込まないような書き込みロジックが必要 考えないといけないことが増える! 8
Slide 9
Slide 9 text
jotai 設計: バリデーション対象は直接の 入力値に限らない © LayerX Inc. 例. 外貨対応の金額入力 ユーザが直接入力した値でなくとも、 ユーザ入力から間接的に決まるものであれば 対象になりうる React Hook Form + Zod だと watch + setValue で つらいことになりがち jotai ならキレイに書ける 9
Slide 10
Slide 10 text
jotai 設計: バリデーション対象は直接の入力値に限らない © LayerX Inc. 例. 外貨対応の金額入力 バリデーション対象であるということは、ドメイン上に概念・名前がある(たぶん) ドメイン上に概念・名前があるなら、コード上でも同じ概念・名前をつけよう バリデーションに埋まったドメインロジックを自然に分離できるのもポジティブ 10
Slide 11
Slide 11 text
現実はもっと複雑 © LayerX Inc. 11
Slide 12
Slide 12 text
例. 外貨対応の金額入力 Lv.2 こうなってくるとテストも大変… React Hook Form での実装例(ちょっと悪意がある) © LayerX Inc. 通貨指定して金額入力できる 日本円換算の金額で設定された 上限バリデーションがある 換算レートは外部から取得する ← New! 12
Slide 13
Slide 13 text
jotai 設計: Derived Atom は 非同期もそのまま扱える jotai での実装例 © LayerX Inc. 例. 外貨対応の金額入力 Lv.2 Derived Atom は Promise を返すことができる derive() を使った async sometimes パターン で Promise | T が返る React につなぐときは必要に応じて Suspend してくれる 非同期の値を書き込むための状態は不要 非同期であっても書き味は大きく変わらない 「データグラフでドメインを自然に表現できる」 という利点 が損なわれない Jotai v2を使いこなすために実は必須級な“async sometimes”パターンの解説 https://zenn.dev/uhyo/articles/jotai-v2-async-sometimes 13
Slide 14
Slide 14 text
現実はまだまだ複雑 © LayerX Inc. 14
Slide 15
Slide 15 text
例. 外貨対応の金額入力 Lv.3 © LayerX Inc. 通貨指定して金額入力できる 日本円換算の金額で設定された 上限バリデーションがある 換算レートは外部から取得する ただし、ユーザが自分で入力することも可能 ← New! e.g. 日本円を現地通貨に両替したときの領収書からレートを決める場合 15
Slide 16
Slide 16 text
jotai 設計: async sometimes atom から 更に derived atom を作れる © LayerX Inc. 例. 外貨対応の金額入力 Lv.3 [async fetchedRate, manualRate] → async rate → async amountJpy atom() が derived() に変わるが、 データの依存グラフを作って必要な値を得る… というモデルは変わらない 16
Slide 17
Slide 17 text
現実は… © LayerX Inc. 17
Slide 18
Slide 18 text
例. 外貨対応の金額入力 Lv.4 © LayerX Inc. 通貨指定して金額入力できる 日本円換算の金額で設定された 上限バリデーションがある 換算レートは外部から取得する ただし、ユーザが自分で入力することも可能 カード決済の場合、金額とレートはカードの決済情報から決まる ← New! 18
Slide 19
Slide 19 text
jotai設計: ユーザが直接編集する状態 と、別の値から決まる値は分ける © LayerX Inc. 例. 外貨対応の金額入力 Lv.4 特に「変更不可能な補完入力」については、 ユーザが入力する状態と分けてあげる 「状態」の責務を減らす 「その値がどこから来たのか」を コード上・動作上いずれにおいてもトレース可能にする 19
Slide 20
Slide 20 text
現実… © LayerX Inc. 20
Slide 21
Slide 21 text
現実… まあ今日は一旦これくらいで © LayerX Inc. 21
Slide 22
Slide 22 text
ここまでのまとめ 現実っぽい例をもとに、jotai の設計プラクティスをいくつか紹介した © LayerX Inc. なるべく状態ではなく値に バリデーション対象は直接の入力値に限らない バリデーションのために設計を歪めない ユーザが直接編集する状態と、別の値から決まる値は分ける 状態の責務を減らし、その値の由来をトレースしやすくしておく Derived Atom は非同期もそのまま扱える async sometimes atom から更に derived atom を作れる 同期・非同期が混ざっていても、コード上は(比較的)滑らかに記述できる 22
Slide 23
Slide 23 text
ここまでのまとめ… からの学び 結局なにが言いたいか © LayerX Inc. なるべく状態(State)ではなく値(Derived Value)に 状態(State)を減らす ユーザが直接編集する状態と、別の値から決まる値は分ける 状態(State)の責務を減らす Derived Atom は非同期もそのまま扱える 非同期処理からの書き込みを回避でき、結果として状態(State)が減る 23
Slide 24
Slide 24 text
「状態」はむずかしい! © LayerX Inc. 「状態」を「読み書き可能な値」ととらえると、「書き込み」の制御が必要になる 「書き込む」という処理を適切に呼び出させないといけない 「状態」の利用者側に「考える」 「適切に使う」という仕事を押し付けている (この 利用者 は AI であることも多くなる… どうやって徹底させるか) なので、「状態」は減らしたい なるべく Derived Value にする jotai であれば、非同期処理が混ざっても、わりと自然に Derived Value として記述できる なくせない「状態」であっても、責務を減らすことで利用者の負担を減らせる 今回の例だと「ユーザが直接編集する状態と、別の値から決まる値は分ける」ことで 状態ごとのユースケースを絞り、利用者の考えることを減らす 24
Slide 25
Slide 25 text
我々は「状態」を減らすために jotai を使っているのかもしれない… © LayerX Inc. 25