はじめに
待ちに待った(?) Suspense for Data FetchingがExperimentalとして先行公開されました。
https://reactjs.org/docs/concurrent-mode-suspense.html
React.Suspense
自体は以前からあり、Data Fetchingについてはどういうものになるか憶測していたのですが、その憶測を上回る状態になって驚きつつ期待もしています。
本投稿では思ったことを雑多に書いていきます。
Render-as-You-Fetch
React Suspenseは仕組み自体は簡単(promiseをthrowできるだけ)なのですが、それによってコーディングパターンは大きく変わります。それがRender-as-You-Fetchというパターンで紹介されています。Suspenseを使う上で、必ずしもこのパターンに従う必要はないのですが、このパターンは様々なメリットがあります。
Suspenseを利用するメリットとしてよく言われるのは、loading stateをまとめて扱えることです。これは、シンプルかつ明確なメリットであり、UXを簡単に改善することができます。また、Render-as-You-Fetchでは、データ取得のタイミングが早いため、アプリによってはUXに影響する改善が見られるかもしれません。
一方で、Render-as-You-FetchではDX改善もされることが期待されます。
もうuseEffectは使わない
React hooksが登場して、非同期処理特にデータ取得については、useEffectを使うことがベストプラクティスとして紹介されてきました。しかし、Suspense for Data Fetchingの登場により、それは過去の話になりそうです。Suspenseではデータ取得のためにuseEffectは使いません。
非同期処理にuseEffectを使うことは有効ではありますが、第二引数のdepsを正しく設定する必要があると言うのが悩ましい問題でありました。間違えると無限ループになったり、exhaustive-depsのルールに従う場合でも思い通りにならなかったりすることがありました。Suspenseではデータ取得はrenderの前に開始されるものであり、depsに頼ることはなくなります。useEffectの利用シーンはいわゆる副作用に限定され、より健全な形になるかもしれません。
react hooksからのthrow promise
Suspenseに対応する文脈で、react hooksでデータ取得を開始して同時にthrow promiseする手法がありますが、これはRender-as-You-Fetchではありません。renderが開始してからデータ取得しているからです。その手法自体に問題があるわけではありませんが、本質的にはSuspenseのパワーを部分的にしか使えていない形になります。ただ、実際は多くのケースでは、renderはそこまで遅くないので、UXには影響しない可能性があります。React.lazyと併用する場合以外は、Render-as-You-FetchによるUXの恩恵は少ないかもしれません。
Render-as-You-FetchによるDXのメリット
一言で言うと、fetchで取得したリモートデータとローカルのデータをほぼ区別することなくコーディングできるのです。コンポーネントのrenderにおいて、リモートデータがまだ取得中なのか取得済みなのかを区別する必要がなくなります。リモートデータは素直に外から与えられるpropsの一つになるか、内部で持つstateの一つになるだけです。
今までのデータ取得の考え方とは異なるので、全く新しいパターンとして認識する必要があります。
完全にシームレスにするにはProxyなどを使う必要があるのですが、そうでなくても、loading stateを無視できると言う点でリモートデータをローカルデータのように扱えるので、メリットがあります。
今後の展望
Render-as-You-Fetchは今後ベストプラクティスを確立していく段階だと思います。現状では、Relayが先行していますが、今後様々な提案がなされるのではないでしょうか。
私自身もこの新しいパターンに対応するライブラリを開発しています。
https://github.com/dai-shi/react-suspense-fetch
codesandboxで動作するリンクも用意していますので、興味ある方はいじってみてください。
おわりに
Suspense for Data Fetchingが来ると分かっている中、React hooksでデータ取得をするためのライブラリreact-hooks-asyncを開発していたのですが、あまりの変わり様に驚いています。いい意味で。react-hooks-asyncの活用の場もないことはないのですが、やはり今後はSuspenseの可能性を探求していきたいと思います。