generated at
Async Selector: APIは「叩く」のではなく「叩かれる」もの


複雑GUIの開発でWeb APIを「叩く」という命令型プログラミングに囚われるとスケールさせるのが難しい
凝ったUIは人の視覚体験へとアプローチする
丁度可知差異を最適化する中で、人間の無意識へ踏み込もうとするしようとする
そうすると設計上の変数が組合わせ爆発を起こす
ユースケースが増えれば増えるほど手続きは比例して増える
手続きに関わる状態が増えれば増えるほど整合性を保つのが難しくなる

ユーザーの利便性を考えるほど、宣言的UIにおける6種のステートのもつれは増える
境界線と言えるものが曖昧となるため複雑性は高まる
依存データフェッチが複雑になりがち
ステップを進んだタイミングではじめて依存値が確定する
依存データはForm Stateだったり、optionalだったりする
依存データの状態遷移で考慮することが増える
いつセットされる?ライフサイクルを把握するのが難しい
コンポーネントのコード読みに行かないとどうなってるかわからない
コンポーネントもServer Stateの管理を意識する必要がある
データフローが追いづらい
コンポーネントツリーの中で解決しようとすると処理が分散してしまう
API系の処理がコンポーネントに委ねられる
イベントハンドラで叩くか、マウントで叩くか?
何度も叩かれないか?
Race Condition対策できてる?
冪等性がある?
エラー時は?
階層構造を持たない独立性の高いUIの状態はLifting state upのようなコンポーネントツリーで管理しきるのは厳しい
親が子の振る舞いに依存するケースがでてきてしまう
そういった場面でReduxは一つの答えだった
しかしServer Stateの管理には難があった

2023年においてはRender As You FetchパターンAtomic State ManagementAsync Selectorによってkoushisa状態管理(GUI)でこまることがかなり減ったのでスナップショット的なものとして残す
Atomic State Managementをいつ使うべきか?については言及しない
以下の意見は、技術の螺旋により過去のものとなる可能性はある

考え方の基礎はReact DocsReduxで培った
前提知識として以下を読んでおくといいかも
---

APIは外界へ接続するインタフェースでありReactとしては副作用として扱う
React主作用State as a SnapshotとしてDOMを構築すること
「叩く」ではなく「叩かれる」というメンタルモデルで扱いたい

宣言的UIの哲学に立ち戻る
>UI = Component(state)
より現代的にすると、
>@dan_abramov: ui = client(server(data), state)
注釈: これはReact Server Componentsでの文脈
このclient()の内側の式がフロントエンドの複雑性のもとである
server(data) * state
ここをシンプルにして考えたい
この式を分割すると以下のように構成できる
md
1. data = (serverState * clientState) // クライアントとサーバーの依存状態をかけあわせる 2. UI = Component(data) // 純粋なレンダリング処理
clientState とは宣言的UIにおける6種のステートにおけるServer State以外の5つを示す
これには揮発的なUI Stateも含む
data の準備が整ってからrenderフェーズへと進みたい
>UIは多くの場合複数の要素の集合体であり、宣言的UIを構築する際はリソースの依存関係を見抜くことが重要
React HooksReact > Contextは依存関係を書くのに適した手法ではない
コンポーネントがデータを所有する = 依存解決はrenderフェーズとなってしまう
いわば 「状態の状態」のようなものが生まれ、その依存関係を把握できなくなる
複数のhooksを組み合わせようとすると途端に辛くなる
上述の 1. data の依存関係を求める式を Async Selector で表現するというのが本スクラップの趣旨
外部リソースへの関心をData-Flow Graphへと分離しSuspenseと組み合わせる
Server StateについてもState as a Snapshotの原則を守る

---

宣言的UI単方向データフロー(unidirectional data flow)ではコンポーネントを主にして考える
コンポーネントがレンダリングだけに集中できるように状態遷移の関心を分離する
1. コンポーネントがイベントをハンドリングする
2. 状態遷移が起こる
3. APIの依存値が変化していたら、「自動で叩かれる」
4. コンポーネントはSuspenseでレンダリングを中断する
5. データが用意できたら新しいデータでレンダリングされる

useEffectでも同じようなことはできるっちゃできるが...
これ起因で無限ループしたというのは誰もが通る道だと思う
他にもいくつか問題がある
1. APIの実行タイミングを制御できない
不要なAPI呼び出しが増えやすい
network waterfallを防ぐのが難しい
依存データフェッチも苦しくなる
2. レスポンスのキャッシュ機構がない
React > Contextを使わない場合は、APIレスポンスの取り回しはpropsとなる
上位概念追加やリファクタがかなり面倒なことになる
APIを叩く箇所部分を移動したときの影響範囲が大きい
コンポーネントのpropsを変更することを強いられる
3. propsバケツリレーになりがち
propsが増えてくると、アプリの状態とUIの状態の区別がつかなくなってくる
本質が見えなくなっていく
propsをuseEffectで監視してAPIを叩く構造は高確率でボトルネックになる
propsバケツリレーについては、カスタムフックLayoutコンポーネントで分割したり、隠蔽すると問題ない場面もあるので否定するわけではない
正し、経験上、一箇所でもpropsをuseEffectで監視すると負の連鎖が続いていい経験はないkoushisa
上手に使う方法よりも、避ける方法を知るべきである

上述の問題を解決するのが宣言的データフェッチ
これでuseEffectの問題点はある程度解決する
代表的な例だとTanStack QueryやSWR
queryKey を使って依存データフェッチが可能
koushisaはこれでも満足できていない部分がある
もうちょっと直感的に書きたい
Form Stateの依存解決も考えると直感的ではない
Form State queryKey に含めるためのhooksやロジックが増える
key管理がノイジーでコード量も増える
Orvalで楽はできるが...
React use apiがきたら多少はマシになるか?
コンポーネントの構造とServer Stateの構造が密結合になりがち
モードレスUIは階層関係を作らない事が多いので、扱いづらい

Async SelectoratomWithAspidaはコンポーネントがレンダリングだけに集中するメンタルモデルへと自然に誘導する
たとえばreact-hook-formRecoilに乗せると
>@koushisa: atomWithReactHookFormの戻り値にはRHFの値と同期しているRecoilのSelectorを含めているのでデータフェッチロジックに組み込める
レンダリングのために値を組み立てる責務やロジック =(プレゼンテーションロジック)をData-Flow Graphへ委譲できる
useQueryの依存値のためにhooksで値を参照してさらにuseEffectで同期するといった黒魔術実装が登場する可能性を減らせる
hooksの絡まりによる中途半端な状態でレンダリングされることはなく、データの一貫性のある状態でレンダリングされる
Tanstack QueryとRecoil/Jotaiの連携でも同様のことができるはず

コンポーネントはどうやってデータフェッチするかやステートのもつれを気にしなくていい
描画とハンドラの構築に集中できる
めんどくさいのをAsync Selectorに分離する
>難しさが廃されるのではなく、難しい部分が一箇所に集中する
時系列で変化する計算結果の依存関係とステート/オブジェクトのライフサイクルが一箇所にまとまる
これは、値というよりは式という言い方のほうがしっくりくる
利用側のことを意識せずにそれ単体で条件や仕様を記述できる
コンポーネントのコードを読みにいかなくても、この値に依存してるんだなと一目でわかる
Server StateForm Stateに依存する必要があったとしても、Data-Flow Graphの仕組みでコンポーネントから見ると疎結合になっている
透過的になる
適宜繋ぎこむまではモックを返すといったことができる
設計の柔軟性が生まれる
アプリ全体を通して純粋関数の割合を増やすことができる

何が言いたかったのか
Async Selectorを利用すると
現代的な宣言的UIを構築する式をReactが唱えるメンタルモデルで自然に作れる
CQSのエッセンスを借りると
制御の反転(IoC)を実現できる
Async SelectorCQSをかけあわせると
クライアントのステートと密結合なデータフェッチ単方向データフロー(unidirectional data flow)にできる
Effect Systemなど難しいことは考えなくて良い