自分が開発しているLaunchableのWebアプリがローンチされて1年半ほどになる。このWebアプリにはReduxのような状態管理ライブラリを入れないまま開発してきたのだが、今のところ困らずに開発できている。そういえば昔自分は状態管理について何か考えていたような…とブログを掘り起こしてみた。
このエントリは2016年にネイティブアプリを対象にして書かれているが、この後自分は2018年ごろにWebフロントエンドに軸足を移し、ネイティブアプリ開発から離れた。なのでこのエントリはWebフロントエンドエンジニアが2022年に再考した話になる。
結論としては、当時自分が管理したかった状態のほとんどは現在ApolloClientのキャッシュによって解決されている。
繰り返しになるが、自分が開発しているLaunchableのWebフロントエンドには状態管理ライブラリが入っていない。しかしグローバルな状態が消えたわけではなく、ApolloClient(他のGraphQLクライアントやSWR、TanStackQuery等に読み替えてもらってもだいたい同じだと思う)、Auth0クライアント、そしてURLの中にある。例えば以下の画面はユーザーのダッシュボードだが、このページに含まれる状態は以下の通りである。
- GraphQLから取ってきたワークスペース情報
- ApolloClientの中にある
- ユーザーの情報
- 認証認可情報、ユーザー名など
- Auth0クライアントの中にある
- 現在選択中のワークスペース名
- URLの中にある(スクショでは見えないが)
- 今回は触れないが、Webアプリはグローバルな状態をURLに持てるのがネイティブアプリとの大きな違いだと思う
認証周りは複雑な状態制御があるので大きなトピックだが、今回はApolloClientの部分に着目する。
2016年当時やりたかったのは主に以下のことなのだが、
- APIから取得した情報が複数の画面でも使われるから、各データとそのロード中、エラーの状態を管理したかった
- ApolloClientはデータのキャッシュだけでなく、ロード中などの状態も管理してくれる
- 複数のAPIの情報を集めて画面が構成されていたので同上
- GraphQLによる一括取得とキャッシュの正規化が解決した
- ユーザーが行った操作によってグローバルな状態が変化したかを管理したかった
- 「いいね」とかユーザー名の変更とか
- Web連携アプリ (ネイティブでもSPAでも)ではユーザーが変更した状態はほぼ全てサーバーに送られるからこれもApolloClientに乗せられる
各項目にもある通り、現在これらはGraphQLとApolloClientによって解決されている。
今でも複数の画面やコンポーネントにまたがるフォームなどではグローバルな状態管理の出番があると思うが、おそらくその場合でもReact Contextのようにスコープを絞った状態管理で間に合うと思われる(もっと複雑化しそうならRecoilになるのかな?まだ必要になっていないので良くわかっていない)。
アプリ開発と状態遷移の管理 - ninjinkun's diary のタイトルにある通り、当時自分は状態「遷移」の管理が重要だと考えており、アプリの中にステートマシンがあるようなモデルをイメージしていた。しかし結局これも「リクエスト→リクエスト中→レスポンス「や「リクエスト→リクエスト中→エラー」という状態遷移がほとんどで、複雑なケースでも複数のAPIリクエストのレスポンスを待ち合わせて合成するような場合であり、結局全て今はApolloClientが管理してくれている。
昔「アプリはでっかいシングルトン」と書いたが、今やそのシングルトンは高性能なキャッシュに姿を変えた。我々プログラマーはGraphQL APIへのリクエストさえ書けば必要な状態は意識しなくても管理されるので、よりUIに注力しやすくなった。Reduxなどを使った状態管理はある種面白くはあったが、手間であることは否めなかったので、自分はこの進化を歓迎している。
別の視点で見てみると、ReduxあたりまではWeb接続を前提としないGUIアプリの文脈で状態を捉えていたが、GraphQLの登場によりWebと連携するアプリはそちらに特殊化する形で状態の問題を解決してしまったとも言えるかもしれない。
自分は最近のネイティブアプリに明るくないのだが、ちょっと探してみた範囲では去年や今年のiOSDCでも同じような内容の発表があったようなので、状況は似てきている気がしている。