これはなに
React でレンダリングされた DOM にhydrate
をする際に、差分があった場合、再レンダリングが行われる。
https://ja.reactjs.org/docs/react-dom.html#hydrate
React はレンダーされる内容が、サーバ・クライアント間で同一であることを期待します。React はテキストコンテンツの差異を修復することは可能ですが、その不一致はバグとして扱い、修正すべきです。開発用モードでは、React は両者のレンダーの不一致について警告します。不一致がある場合に属性の差異が修復されるという保証はありません。これはパフォーマンス上の理由から重要です。なぜなら、ほとんどのアプリケーションにおいて不一致が発生するということは稀であり、全てのマークアップを検証することは許容不可能なほど高コストになるためです。
そう思って、hydrate
をしていたところ、再レンダリングがされない場合があったため、その場合の条件と推測を記事にする。
今回、検証を行ったサンプルコードは以下。
どんなときに再レンダリングがされないのか
dangerouslySetInnerHTML
で要素を追加している場合、dangerouslySetInnerHTML
プロパティがついている要素配下で差分が生じていても再レンダリングされないことがわかった。
以下は、サーバーでレンダリングしたときはserver
、クライアントでレンダリングしたときはclient
と表示されるコンポーネント。
import React from "react";
const App = () => {
let d = "";
if (typeof window === "undefined") {
d = "server";
} else {
d = "client";
}
return (
<>
{/* ↓再レンダリングされない */}
<div
style={{ backgroundColor: "pink" }}
dangerouslySetInnerHTML={{ __html: d }}
></div>
{/* ↓再レンダリングされる */}
<div>{d}</div>
</>
);
};
export default App;
↓ が実際の結果のスクリーンショットだが、dangerouslySetInnerHTML
で要素を追加した方(背景がピンク)のみ再レンダリングがされていない。
なぜ再レンダリングされないのか?(推測)
React のソースコードをすべて追いきれなかったので推測になるが、dangerouslySetInnerHTML
のソースを読んでみると、単純にinnerHTML
を読んでいるだけの様。
React が Fiber ツリーをもとにレンダリングしていることを考えると、dangerouslySetInnerHTML
で追加した要素は Fiber ツリーの管理下にないのではないかと思っている(もしくは、dangerouslySetInnerHTML
配下は any として扱う)。
その結果、ツリーと実際の DOM を比較しても差分を検出できず、今回のようになってしまう。というのが推測だ。
(もし知っている方がいれば教えていただけるととても助かります!)
最後に
React のソースコードを追いきれず、真の理由を知れなかったのは残念だった。
(React のソースコードを読むのは疲れる。)
もし知っている方がいれば教えていただけるととても助かります!