React のハイドレーションとは?
ハイドレーションとは?
事前に生成された HTML に対してイベントハンドラをアタッチしていく処理のことです。
以下に HTML があります。これは事前に生成された HTML を想定しています。
この HTML はイベントハンドラがアタッチされていないため、ボタンをクリックしても何も起きません。
<!DOCTYPE html>
<html>
<head>
<title>My app</title>
</head>
<body>
<div id="root">
<h1>Hello</h1>
<button>
You clicked me
<!-- -->0<!-- -->
times
</button>
</div>
</body>
</html>
この状態の HTML に対して、イベントハンドラをアタッチしていくのがハイドレーションであり、例えば、<button>
に対して、クリックイベントをアタッチします。
なぜハイドレーションをするのか?
SSR をするためです。
と言えばそれまでですが、せっかくなのでハイドレーションを使用しない場合の、通常の React アプリケーションがブラウザに表示されるプロセス(CSR)の問題点を提示した上でハイドレーションをする理由を書いていきます。
※ SSR : サーバーサイドレンダリング
※ CSR : クライアントサイドレンダリング
図はShaundai Person さんのプレゼン の資料を拝借
上記の図の "Interactive" は、HTML にイベントハンドラがアタッチされて、ユーザーがボタンをクリックすれば、それに応じたアクションが起きる状態です。
CSR の問題点は、"Interactive" の状態になるまでブラウザは真っ白であることです。
このブラウザが真っ白な状態は、以下のとてもシンプルな React アプリケーション簡単に再現させることができます。
このシンプルな React のアプリケーションはとても小規模なため、相当にネットワーク速度が遅くとも、端末の性能が悪くとも、一瞬で "Interactive" な状態になります。
1) Load JS
2) Fetch Data
3) Render Components
4) Interactive ← 一瞬でここまで来る
ブラウザを真っ白な状態にするために、少し工夫をします。ブラウザの JavaScript を無効にする設定をすれば "Interactive" な状態になりません。以下は、実際にやってみた画像です。
このように "Interactive" な状態になるまで真っ白な状態になります。大規模なアプケーションの場合やユーザーのネットワーク環境によっては、真っ白な時間は長くなる可能性があります。その間、ユーザーは暇です。人によっては離脱するかもしれません。
この CSR の問題点を解消するために、"Load JS" より手前で、事前に生成された HTML を表示します。
そうすることで、"Interactive" な状態まで真っ白な状態が続くことを避けることができます。
0) 事前に生成された HTML を表示 ← new!!
1) Load JS ← 0) が表示されている
2) Fetch Data ← 0) が表示されている
3) Render Components ← 0) が表示されている
4) Interactive
React でやってみる
hydrateRoot を使います。
使い方はとてもシンプルです。まずは、以下の public/index.html
に初期状態で表示したい HTML を置きます。
<div id="root">
<h1>Hello, world!</h1>
<button>
You clicked me <!-- -->0<!-- --> times
</button>
</div>
そして、初期状態で表示したい HTML を以下のように hydrateRoot に渡します。
hydrateRoot(
document.getElementById('root'),
<App />
);
これで "Load JS" の手前で public/index.html
が表示され、ユーザーが真っ白な状態を見続けることを防ぐことができます。
0) 事前に生成された HTML を表示
1) Load JS ← 0) が表示されている
2) Fetch Data ← 0) が表示されている
3) Render Components ← 0) が表示されている
4) Interactive
注意点として、hydrateRoot を使う場合は、"Interactive" な状態になった場合に初期状態で表示した HTML と差分があってはいけません。最終的に出来上がる React コンポーネントは以下のように HTML と合わせる必要があります。
import { useState } from 'react';
export default function App() {
return (
<>
<h1>Hello, world!</h1>
<Counter />
</>
);
}
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
You clicked me {count} times
</button>
);
}
ハイドレーションの問題点
hydrateRoot を使うことで、CSR と異なり、"Interactive" な状態になるまで真っ白な状態を防ぐことに成功はしました。ただ、HTML を表示しただけで、"Interactive" な状態ではないため、表示されているボタンを触ったとしても何も起きません。
0) 事前に生成された HTML を表示
1) Load JS
2) Fetch Data
3) Render Components
4) Interactive ← 表示されたボタンが有効になるのはこのタイミング
hydrateRoot を使ったとしても "Interactive" な状態になるまでの時間が短くなるわけではないです。
むしろ、表示されているボタンを触ったとしても何も起きないことにイライラするユーザーもいるかもしれません。そのため、"Interactive" な状態になるまでの時間が長すぎる場合は、他の方法で解決を試みる必要があります。
まとめ
React の hydrateRoot を使ったアプローチは、SSR with hydration に当たると私は理解しています。
図はWeb 上でのレンダリングから拝借
React の hydrateRoot を使ったアプローチも記事で示したようにメリット・デメリットがあります。
昨今は、レンダリングに関する手法が様々あるため、本記事では少し古めの内容ですが React のハイドレーションに観点を絞ってまとめてみました。
参考
Discussion
こちらの参考記事を見る限りだとハイドレートという単語だけでTakahiroさんの書いている意味を表すと思います。
つまり、「ハイドレーションする」という言い回しだと「頭痛が痛い」のように二重表現になるという事でしょうか?