React.Suspenseの使い所で思ったところ(個人的なメモ)
単純なUX観点でのReact.Suspenseの使い所メモ
Suspenseは確かに利点は多い、今までデータフェッチングにおいて、データの状態を意識する必要があり、都度対応に迫られることが多かった。
isLoading、isFetchingなどの状態。がSuspenseはpromiseがpendingの状態はfallbackコンポーネントに差し代わるため、このpromiseの状態を意識する必要がない点は非常にストレスフリー。
ただ、エラーハンドリングを意識するとそれは途端に難しいものになる印象。
元々Suspenseはfallbackコンポーネントに丸ごと置き換わるため(fallbackコンポーネントがSuspenseのchildrenにオーバーレイするような利用はできない*1)、アプリケーション全体をSuspenseでラップして、最上位で全てのpromiseをハンドリングする使い方は好ましくない。
colocationという名前で知られている通り、今のReactの設計は「データフェッチングは、そのデータが必要なコンポーネントで行う」とう方向に進んでいる。
つまり、Suspenseは局所的な使い方が推奨されている。確かにその方が他のコンポーネントの描画がブロックされず、UXとしてもはるかに良い。
ここでエラーハンドリングは?といった考えが浮かんでくる。
データフェッチングでエラーになり、例外がthrowされた場合、Suspenseではキャッチすることはできないため、これはやはりErrorBoundaryの役目になると思うが、ではこのErrorBoundaryをこれも各所に配置するのかどうかというところ。
自身の結論としては「各所に配置してエラー境界を分ける必要がある」と思う結果になった。
なぜかというと例外が発生した際にthrowせず、ハンドリングを行うと考えもあるが、そうなると結局、データに対して取得できなかったケースを想定してコンポーネントを書かなければならず、Suspenseの利用するメリットが薄れる気がしており、suspendされている間はfallbackコンポーネント、例外が発生したらErrorBoudaryに委ね、promiseが正常にresolveされたケースだけを考えてコンポーネントを書く方がはるかに楽だし、それがSuspenseを利用する大きなメリットになっているとも思っている(並行レンダリング云々のメリット享受の話は置いておき、UX観点)。
同じようなissueもあった
そうなると必然的にSuspenseとErrorBoudaryはセットで利用したくなる。
以下のような形
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<Suspense fallback={<div>Loading...</div>}>
<SuspendComponent />
</Suspense>
</ErrorBoundary>
となるともう個別に記述するのは面倒なため、
const SuspenseBoudary = ({ errorFallback, suspenseFallback, children }) => {
return (
<ErrorBoundary fallback={errorFallback}>
<Suspense fallback={suspenseFallback}>
{children}
</Suspense>
</ErrorBoundary>
)
}
もっとするとDecoratorにしてSuspendされるコンポーネントに宣言的に利用できるようにしてもいいのかもしれない。
Suspenseにおけるエラーハンドリングについて最適化を模索中、、、