hydrateRoot
hydrateRoot
を使用すると、react-dom/server
によって事前生成した HTML コンテンツが含まれるブラウザ DOM ノード内に、React コンポーネントを表示できます。
const root = hydrateRoot(domNode, reactNode, options?)
リファレンス
hydrateRoot(domNode, reactNode, options?)
hydrateRoot
を呼び出して、サーバ環境で事前に React がレンダーした HTML に対して React を「アタッチ」します。
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, reactNode);
React は、domNode
内に存在する HTML にアタッチし、その内部の DOM の管理を引き継ぎます。React で完全に構築されたアプリには、ルートコンポーネントを引数にした hydrateRoot
呼び出しが通常 1 つのみ存在します。
引数
-
domNode
: サーバ上でルート要素としてレンダーされた DOM 要素。 -
reactNode
: 既存の初期 HTML をレンダーするために使用された “React ノード”。これは通常、ReactDOM Server
のメソッド(例:renderToPipeableStream(<App />)
)でレンダーされた JSX、例えば<App />
になります。 -
省略可能
options
: この React ルートのオプションを含むオブジェクト。- 省略可能
onCaughtError
: エラーバウンダリ内で React がエラーをキャッチしたときに呼び出されるコールバック。エラーバウンダリにキャッチされたerror
と、componentStack
を含んだerrorInfo
を引数にして呼び出されます。 - 省略可能
onUncaughtError
: エラーがスローされがエラーバウンダリでキャッチされななかったときに呼び出されるコールバック。スローされたerror
と、componentStack
を含んだerrorInfo
を引数にして呼び出されます。 - optional
onRecoverableError
: React が自動的にエラーから回復したときに呼び出されるコールバック。React がスローするerror
と、componentStack
を含んだerrorInfo
を引数にして呼び出されます。復帰可能なエラーの一部は元のエラーをerror.cause
として含んでいます。 - 省略可能
identifierPrefix
: React がuseId
によって生成する ID に使用する文字列プレフィックス。同じページ上に複数のルートを使用する際に、競合を避けるために用います。サーバ上で使用されたものと同じプレフィックスでなければなりません。
- 省略可能
返り値
hydrateRoot
は、render
と unmount
の 2 つのメソッドを持つオブジェクトを返します。
注意点
hydrateRoot()
は、レンダーされたコンテンツがサーバでレンダーされたコンテンツと同一であることを期待しています。不一致はバグとして扱い修正する必要があります。- 開発モードでは、React はハイドレーション中の不一致について警告します。不一致が発生した場合、属性の違いが修正される保証はありません。これはパフォーマンス上の理由から重要です。なぜならほとんどのアプリでは、不一致はまれであり、すべてのマークアップを検証することは非常に高コストになるからです。
- アプリには通常、
hydrateRoot
呼び出しは 1 つだけ存在します。フレームワークを使用している場合、フレームワークがこの呼び出しを行うかもしれません。 - アプリがクライアントでレンダーする形式であり、事前レンダーされた HTML がない場合、
hydrateRoot()
は使用できません。代わりにcreateRoot()
を使用してください。
root.render(reactNode)
root.render
を呼び出して、ブラウザ DOM 要素内にハイドレーションされた React ルート内の React コンポーネントを更新します。
root.render(<App />);
React はハイドレーションされた root
内の <App />
を更新します。
引数
reactNode
: 更新したい “React ノード”。通常は<App />
のような JSX ですが、createElement()
で構築した React 要素や、文字列、数値、null
、またはundefined
を渡すこともできます。
返り値
root.render
は undefined
を返します。
注意点
- ルートがハイドレーションを完了する前に
root.render
を呼び出すと、React は既存のサーバレンダリングされた HTML コンテンツをクリアし、ルート全体をクライアントでのレンダーに切り替えます。
root.unmount()
root.unmount
を呼び出して、React ルート内にレンダーされたツリーを破棄します。
root.unmount();
React で完全に構築されたアプリには、通常、root.unmount
の呼び出しは一切ありません。
これは主に、React ルートの DOM ノード(またはその祖先のいずれか)が他のコードによって DOM から削除される可能性がある場合に有用です。例えば、非アクティブなタブを DOM から削除する jQuery のタブパネルがあると想像してみてください。タブが削除されると、(React ルートを含んだ)内部のすべてが DOM から削除されます。その場合、削除されたルートのコンテンツの管理を「停止」するよう React に伝えるために root.unmount
を呼び出す必要があります。そうしないと、削除されたルート内のコンポーネントは、データ購読などのグローバルリソースをクリーンアップして解放する必要があるということが分からないままになります。
root.unmount
を呼び出すことで、ルート内のすべてのコンポーネントがアンマウントされ、React がルート DOM ノードから「切り離され」ます。これには、ツリー内のイベントハンドラや state の削除も含まれます。
引数
root.unmount
は引数を受け付けません。
返り値
root.unmount
は undefined
を返します。
注意点
-
root.unmount
を呼び出すと、ツリー内のすべてのコンポーネントがアンマウントされ、React がルート DOM ノードから「切り離され」ます。 -
root.unmount
を呼び出した後、同一ルートで再度root.render
を呼び出すことはできません。アンマウント済みのルートでroot.render
を呼び出そうとすると、“Cannot update an unmounted root” というエラーがスローされます。
使用法
サーバでレンダーされた HTML のハイドレーション
あなたのアプリの HTML が react-dom/server
によって生成されている場合、クライアント上それに対するハイドレーションを行う必要があります。
import { hydrateRoot } from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);
これにより、ブラウザ DOM ノード内にあるサーバからの HTML がハイドレーションされ、あなたのアプリの React コンポーネントとなります。通常、これはスタートアップ時に一度のみ行います。フレームワークを使用している場合はあなたの代わりにこの呼び出しが自動で行われるかもしれません。
アプリのハイドレーション時に React は、サーバで生成された初期 HTML に、あなたのコンポーネントのロジックを「アタッチ」します。ハイドレーションにより、サーバからの初期 HTML スナップショットが、ブラウザで動作する完全にインタラクティブなアプリに変化します。
import './styles.css'; import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; hydrateRoot( document.getElementById('root'), <App /> );
通常、hydrateRoot
を再度呼び出したり、複数の場所で呼び出したりする必要はありません。ここから先は、React がアプリケーションの DOM を管理しています。UI を更新するには、コンポーネントは state を使うことになるでしょう。
ドキュメント全体のハイドレーション
React で完全に構築されたアプリケーションは、<html>
タグを含むドキュメント全体を JSX としてレンダーすることができます。
function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<Router />
</body>
</html>
);
}
ドキュメント全体をハイドレートするには、hydrateRoot
の最初の引数として document
グローバル変数を渡します。
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />);
やむを得ないハイドレーション不一致エラーの抑制
サーバとクライアントの間で、単一の要素の属性やテキストコンテンツがやむを得ない理由で異なる場合(たとえば、タイムスタンプなど)、ハイドレーションの不一致警告を抑制することができます。
要素のハイドレーション警告を抑制するには、suppressHydrationWarning={true}
を追加します。
export default function App() { return ( <h1 suppressHydrationWarning={true}> Current Date: {new Date().toLocaleDateString()} </h1> ); }
これは単一レベルの深さまでしか機能せず、避難ハッチとしての使用を意図しています。過度に使用しないでください。これを使用してもテキストコンテンツ以外の場合は React は違いを修正しようとはしないため、将来の更新まで一貫性が保たれない可能性があります。
クライアントとサーバで異なるコンテンツの処理
サーバとクライアントで意図的に異なるものをレンダーする必要がある場合、2 回に分けたレンダーを行うことができます。クライアントで異なるものをレンダーするコンポーネントは、isClient
のような state 変数を読み取るようにし、この変数をエフェクト内で true
に設定することができます。
import { useState, useEffect } from "react"; export default function App() { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); return ( <h1> {isClient ? 'Is Client' : 'Is Server'} </h1> ); }
この方法では、初回のレンダーはサーバと同じコンテンツをレンダーし、不一致を回避しますが、追加のレンダーがハイドレーションの直後に同期的に行われます。
ハイドレーションされたルートコンポーネントの更新
ルートのハイドレーションが完了した後、root.render
を呼び出してルートの React コンポーネントを更新することができます。createRoot
の場合とは異なり、初期コンテンツはすでに HTML としてレンダーされているため、通常はこれを行う必要はありません。
ハイドレーションの後のどこかのタイミングで root.render
を呼び出し、コンポーネントツリーの構造が以前にレンダーされたものと一致していれば、React は state を保持します。以下の例で入力フィールドにタイプできることに着目してください。つまり毎秒 render
が繰り返し呼び出されていますが、更新により DOM が破壊されていないということです。
import { hydrateRoot } from 'react-dom/client'; import './styles.css'; import App from './App.js'; const root = hydrateRoot( document.getElementById('root'), <App counter={0} /> ); let i = 0; setInterval(() => { root.render(<App counter={i} />); i++; }, 1000);
ハイドレーションされたルートで root.render
を呼び出すことは滅多にありません。通常、代わりにコンポーネントの中で state を更新 します。
キャッチされないエラーに対するダイアログを表示
デフォルトでは React は、キャッチされなかったエラーをコンソールにログとして表示します。独自のエラーレポーティングを実装するには、省略可能なルートオプションである onUncaughtError
を指定します。
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onUncaughtError: (error, errorInfo) => {
console.error(
'Uncaught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
onUncaughtError オプションに指定するのは、以下の 2 つの引数を付けて呼ばれる関数です。
- スローされた error。
- errorInfo オブジェクト。エラーの componentStack を含んでいる。
エラーダイアログを表示するために onUncaughtError
ルートオプションを用いることが可能です。
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportUncaughtError} from "./reportError"; import "./styles.css"; import {renderToString} from 'react-dom/server'; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onUncaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportUncaughtError({ error, componentStack: errorInfo.componentStack }); } } });
エラーバウンダリでキャッチしたエラーを表示
デフォルトでは、React はエラーバウンダリによってキャッチされたすべてのエラーを console.error
に記録します。この動作をオーバーライドするには、省略可能なルートオプションである onCaughtError
を指定して、エラーバウンダリによってキャッチされたエラーを処理するようにします。
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onCaughtError: (error, errorInfo) => {
console.error(
'Caught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
onCaughtError オプションに指定するのは、以下の 2 つの引数を付けて呼ばれる関数です。
- バウンダリによってキャッチされた error。
- errorInfo オブジェクト。当該エラーの componentStack を含んでいる。
onCaughtError
ルートオプションを用いて、エラーダイアログを表示したり、既知のエラーをログから除外したりできます。
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportCaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onCaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportCaughtError({ error, componentStack: errorInfo.componentStack }); } } });
復帰可能なハイドレーション不一致エラーに対するダイアログを表示
React がハイドレーション時に不一致を検出した場合、自動的にクライアント側でレンダーの復帰をを試みます。デフォルトでは、ハイドレーション不一致に関するエラーは console.error
に記録されます。この動作をオーバーライドするには、省略可能なルートオプションである onRecoverableError
を指定します。
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onRecoverableError: (error, errorInfo) => {
console.error(
'Caught error',
error,
error.cause,
errorInfo.componentStack
);
}
}
);
onRecoverableError オプションに指定するのは、以下の 2 つの引数を付けて呼ばれる関数です。
- React たスローした error。一部のエラーは元のエラーを error.cause として含んでいる。
- errorInfo オブジェクト。エラーの componentStack を含んでいる。
onRecoverableError
を用いてハイドレーション不一致に関するエラーダイアログを表示することができます。
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportRecoverableError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onRecoverableError: (error, errorInfo) => { reportRecoverableError({ error, cause: error.cause, componentStack: errorInfo.componentStack }); } });
Troubleshooting
“You passed a second argument to root.render” というエラーが出る
よくある間違いとして、hydrateRoot
用のオプションを root.render(...)
に渡してしまうことが挙げられます。
修正するには、ルートオプションを hydrateRoot(...)
に渡すようにしてください。root.render(...)
ではありません。
// 🚩 Wrong: root.render only takes one argument.
root.render(App, {onUncaughtError});
// ✅ Correct: pass options to createRoot.
const root = hydrateRoot(container, <App />, {onUncaughtError});