Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Suspenseのユースケースを探る

 Suspenseのユースケースを探る

KeitaroOkamura

February 08, 2022
Tweet

More Decks by KeitaroOkamura

Other Decks in Technology

Transcript

  1. 知らない方のために • 「Suspense」 は次期メジャーバージョンの React 18 で大きく拡張された機能 ◦ 以前から存在していたが、それまではコンポーネントの Dynamic

    Import のために使われていた • React 18 では Server-Side Rendering (SSR) のストリーミングサポートが追加される ◦ この SSR ストリーミングは Suspense を前提にしている ◦ また、「React Server Components」 という技術的革新においても Suspense(正確に言えば、React Concurrent Rendering という機能)が重要な役割を果たします • つまり ◦ 技術的な転換期に入ってきており(SSG → SSRという流れが強くなっている)様々なステート管理や データ取得ライブラリが Suspense を含む React 18 への対応を始めている ◦ Suspense のおさらいをしないと僕みたいに取り残されますw
  2. Suspense とは何か 簡単に言うと • コンポーネントを「ローディング中なのでまだレンダリングできない」 という状態にすることができるのが Suspense です • Suspense

    というコンポーネントが、内部でレンダリングが失敗したコン ポーネントのハンドリングを担当してくれます ◦ まるで JavaScript の try-catch 文のような振る舞い ◦ React の既存機能である ErrorBoundary と Suspense の仕組みは 似ていて、キャッチする対象が違うだけ ローディング中 <Suspense> </Suspense> キャッチ <ErrorBoundary> エラー </ErrorBoundary> キャッチ
  3. Suspence のユースケース • サスペンスは、データも含むその他あらゆるものを宣言的に「待機」するための機能 ◦ メインのユースケースはデータ取得だが、画像やスクリプト、あるいはその他の非同期的な作業の待機 にも使える ◦ つまり、コンポーネント以外のリソースの取得を待機することができるようになった Suspense

    for Data Fetching is a new feature that lets you also use <Suspense> to declaratively “wait” for anything else, including data. This page focuses on the data fetching use case, but it can also wait for images, scripts, or other asynchronous work. https://reactjs.org/docs/concurrent-mode-suspense.html 公式には以下のように書いてある
  4. 1. コンポーネントが「ローディング中」を宣言する部 分。それは、コンポーネントがレンダリング中に Promise を throw することで行う 2. Suspenseコンポーネント自体。このコンポーネント で囲った部分でサスペンド※が発生した場合、指定

    しているフォールバックコンテンツが代わりにレン ダリングされる ※Promise が解決するまではコンポーネントをレンダリングできない状態 </Suspense> <Suspense fallback={<div>loading…</div>}> <div>loading…</div> Suspense のAPI は主に2つの要素で成り立つ <子コンポーネント /> ローディング中 Promiseをthrowする フォールバックコンテンツを指定 Suspense の仕組み キャッチ
  5. Suspence の仕組み • Promise を投げるのは、それをキャッチした Suspense に「このコンポーネントはいつレンダリングができ るのか」を伝えるため ◦ 投げられた

    Promise が解決されたら、それを投げた子コンポーネントのレンダリングをリトライする という仕組みになっている • render やライフサイクルメソッドでエラーが起きたときに、それを親コンポーネントの componentDidCatch※というライフサイクルメソッドでキャッチするという仕組みで Suspense は動きます ※React で子コンポーネントのランタイムエラーをキャッチするためのハンドラ 詳しく説明すると
  6. • アンマウント ◦ コンポーネントライフサイクルメソッドの一つ => componentWillUnmount() ◦ コンポーネントが DOM から削除されるときに呼ばれる

    • つまり、Suspense によって置き換えられた子コンポーネントは UI が破棄されない ◦ UI が破棄されないので、画面の状態※を残したままにできる ◦ 何度も再描画されてほしくなく、状態を保持したままでいてほしいようなケース ◦ ※どういったケースで使えるのか、実際に実験してみる Suspense によって置き換えられた子コンポーネントは、アンマウントされない Suspense コンポーネントの特徴
  7. • アンマウントされると UI が破棄されるので、入力中の input の値や、スクロール位置などの状態が破棄されてしまう • 同様なケースとしては、タブ切り替えなど • 破棄されるのは

    UI の状態だけ、入力中の値やスクロール位置 を State(コンポーネントによって管理されているオブジェ クト) で管理していれば破棄されない 何が困るか? Suspense のユースケースを探る
  8. • Suspense を利用したコンポーネントを作ってみる • このコンポーネントの中身は、props で受け取った値に応じ て空の Promise を throw

    するというシンプルな中身 ◦ Loadable が ローディング中であることを Promise を throw することで Suspense に伝える ◦ Suspense は その Promise を監視する ◦ fallback は指定しなかったら何も表示されない Promise を throw すればいいってことはわかった Suspense を使ってみる
  9. • アンマウントされないことで、ある瞬間にユーザーから見えない部分について、不要な再レンダリングを回避で きる ◦ つまり、結果として React が不要な計算(Reconciliation のような差分検出処理)をするのを省くことが できる ◦

    今回の実験したケースでは、DOM自体が大きくないので、そこまでパフォーマンスに影響はないかもしれ ない(一つのコンポーネントで管理する派の人たちもいるはず) • しかし、忘れてはいけない制限として ◦ Suspense で囲った子コンポーネントは 一時的に画面から削除され、プレースホルダーに置き換えられる ◦ つまりは、通常はユーザーに表示されないような部分があるケースのみ、Suspense の性質を利用するこ とができる UI を保持すること以外にもうまみがある Suspense のうまみ
  10. • 最もふさわしいユースケースはネイティブアプリの Stack 型のナビ ゲーション ◦ このSuspenseの性質を利用することで Stack の一番上にある コンポーネント以外を停止することで、効率良く画面の状態

    をロックすることができる • React Native の contributor の人が React Native 向けにコンポー ネント作ってました ◦ https://github.com/software-mansion/react-freeze それネイティブアプリじゃん 通常はユーザーに表示されないような部分があるケース
  11. • Suspenseはデータ取得以外のユースケースにも活用できる • Promise を throw すること自体は難しくないが、非同期のデータ取得(データフェッチ系)が絡むと 一から書くのは難しそう ◦ そこは素直にライブラリ使ったほうがいいと思う

    • Suspense を使うことで、今までは ローディング の状態を判定して処理をしていたような、手続き的 なコードがより宣言的に書けるようになる • Suspense によって今までのコンポーネントの責務が大きく変わっていきそう(コンポーネントがロー ディング中の表示の責務を持たないとか) まとめ
  12. 参考 • React 18に備えるにはどうすればいいの? 5分で理解する ◦ https://qiita.com/uhyo/items/bbc22022fe846fd2b763 • ReactのSuspense対応非同期処理を手書きするハンズオン ◦ https://zenn.dev/uhyo/books/react-concurrent-handson

    • Experimenting with React Freeze ◦ https://blog.swmansion.com/experimenting-with-react-freeze-71da578e2fa6 • ソースコード ◦ https://github.com/KeitaroOkamura/suspense-usecases