<Suspense>を再履修する

Suspenseを久しく使っていないので、再履修しておこうと思います。
[公式 : Suspense](https://ja.react.dev/reference/react/Suspense#revealing-content-together-at-once)

----

#### そもそもサスペンスの意味は？
> 宙ぶらりんの状態、あやふや、どっちつかず、不安、気がかり、持続的緊張感、はらはらする状態
[weblio](https://ejje.weblio.jp/content/suspense)

(サスペンス映画とか言われますね。)

## Suspense

> `<Suspense>` を使うことで、子要素が読み込みを完了するまでフォールバックを表示させることができます。

なるほど、子要素が重い時、読み込みに時間がかかる時にSuspenseを使って別の何かを出すことができるということですね。

### propsは2つ
- children
    - レンダーしようとしている実際の UI (コンポーネント)
- fallback 
    - childrenのレンダーが完了するまで代わりにレンダーされるもの。
        - ローディングスピナやスケルトン

```jsx
// 実装イメージ
<Suspense fallback="ローディング">
  <SomeComponent />
</Suspense>
```

### 注意点

>React は、初回マウントが成功するより前にサスペンドしたレンダーに関しては、一切の state を保持しま〜...

長いですね、、つまり下記の4つです。

#### 初回マウント前のサスペンド
コンポーネントが初回マウント前にサスペンドすると、Reactはその時点のstateを保持せず、再度レンダリングを最初からやり直します。

```jsx
// Suspenseのchildren
const SomeComponent = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData().then(setData);
  }, []);

  if (!data) {
    // 一旦ここまで処理は進むけど、サスペンドが発生。再レンダリングされる。
    // 次のレンダリングはデータの取得を待ってからとなる。
    throw new Promise(resolve => setTimeout(resolve, 1000));
  }

  return <div>{data}</div>;
}
```

#### サスペンドとフォールバック
一度表示されたコンテンツが再度サスペンドした場合、通常はフォールバックが再び表示されます。ただし、この更新がstartTransitionやuseDeferredValueによって引き起こされた場合はしません。

#### レイアウトエフェクトのクリーンアップ
サスペンドによってコンテンツが隠された場合、Reactはレイアウトエフェクトをクリーンアップし、再表示時に再実行します。無駄なDOM操作はしないよということです。

#### 最適化
Reactには、サスペンスと統合された自動的な最適化（ストリーミングサーバレンダリングや選択的ハイドレーション）が含まれています。


## 使用方法

```jsx
<Suspense fallback="ローディング">
  <SomeComponent />
</Suspense>
```

子要素がすべて読み込まれるまで、フォールバックを表示します。

#### 補足

>サスペンスコンポーネントをアクティブ化できるのはサスペンス対応のデータソースだけです。これには以下が含まれます~...

つまり、 `サスペンス対応のフレームワークしか使えないよ` ということです。
Next.js, Remix, Relay は使えるみたいですね。
(useSWRやuseQueryを用いれば、フレームワーク問わず使えます)

### コンテンツを一度にまとめて表示する

> デフォルトでは、サスペンス内のすべてのツリーはひとつの単位として扱われます。例えば、以下のコンポーネントのうちどれかひとつでもデータ待ちでサスペンドしていれば、すべてがまとめてローディングインジケータに置き換わります。

ということは、Suspenseに巻き込みたくないコンポーネントは、`<Suspense>`の外に出しておくのが良さそうですね。
非同期するコンポーネントが複数ある場合、準備が出来たものから表示したいのですが...。

### ネストされたコンテンツをロード順に表示する 

Suspenseの中にSuspenseを入れる...で、できるみたいです。
下記の実装では `<Biography>` と `<Albums>` はそれぞれ準備でき次第表示されます。

```jsx
// 公式から引用
<Suspense fallback={<BigSpinner />}>
  <Biography />
  <Suspense fallback={<AlbumsGlimmer />}>
    <Panel>
      <Albums />
    </Panel>
  </Suspense>
</Suspense>
```

### 新しいコンテンツのロード中に古いコンテンツを表示する

入力するたびにfallbackが表示されてしまうのは良くありません。
次の情報の準備が整うまで、1つ前の情報を出しっぱなしにしたいですね。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129861/1dd26ee9-4316-bec4-319d-83798ce16526.gif" width="500">

[useDeferredValue](https://ja.react.dev/reference/react/useDeferredValue) フックを使うことで遅延されたバージョンのクエリ文字列を下に渡すことができます。(useDeferredValueのドキュメントも見ておかないと

![download.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129861/44b47970-58dd-29a8-ea5d-b893871bbc27.gif)

## 終わりに
本日は `<Suspense>`を再履修しました！
非同期データの読み込み中にユーザーにフォールバックUIを提供し、ローディングスピナーやプレースホルダーを表示することで、UIが空白にならないようできます！
ユーザーが「今何を待っているんだろう...」と不安にならないよう、状態を可視化してあげることがとても大事ですね。

Reactでの開発を進める際には、適切な場所で`<Suspense>`を使って、アプリケーションのパフォーマンスとユーザーエクスペリエンスを最適化しましょう！

合わせて[startTransition](https://ja.react.dev/reference/react/startTransition)と、[useDeferredValue](https://ja.react.dev/reference/react/useDeferredValue)もまた学んでおきましょう〜！