🏖️

Reactのレンダリングを理解する

2022/07/10に公開

はじめに

Reactが指す「レンダリング」ちゃんと理解できていますか?
レンダリングって画面を描画することですよね?と思ったそこのあなた。それはブラウザレンダリングがやっているのでReactはやっていません。
いやいやレンダリングはDOMに反映することだと思ったそこのあなた。
実はそれも微妙に違います。なぜなら レンダリングが必ずしもDOMへの反映に繋がる わけではないからです。

似た様な言葉が多く私自身混同してきたのでまとめたいと思います。

きっかけ

https://zenn.dev/yodaka/articles/7c3dca006eba7d

私が混乱したきっかけは上記記事を読んだ時です。そのコメント欄にて以下のような話題がありました。

render(ing) を「レンダー」と「描画」で、意図的に訳し分けるルールにしています。前者は React が render() や関数コンポーネントの本体を呼び出すこと、後者はブラウザが画面に DOM を反映する動作のこと、というルールになっています。元々の英語ドキュメントの時点でも、render の意味するところが文脈によって曖昧でたまに何を指すのかよく分からないという問題がありまして(「render という動詞がオーバーロードされている!」と文句言われてました)、日本語独自で上記のような使い分けを導入しています。

どうやらReactにおいて、render を指す意味が2つ存在するせいで、話をややこしくしているようです。たしかにレンダリングについて色々調べてみると、日本語の記事も両者の意味が混在していて、ズレが生まれているように感じました。

このズレを埋めるためにも最も信頼できる公式Docmentを読んでいきたいと思います。

※ 備考
https://beta.reactjs.org/
Reactの公式ドキュメントにはbeta版があります。英語ではありますが今回は最新の情報を見ていきたいのでbeta版を参考にまとめていきたいと思います。beta版のため今後情報が変わる可能性がある点にはご了承ください。

また、レンダー(render)とレンダリング(rendering) は同義として扱います。

Reactが定義するレンダリングとは?

React Docs BETA には以下の様に記載されています。

“Rendering” is React calling your components.

訳してみると 「コンポーネントをReactが呼び出すこと」
つまりReactがコンポーネントを呼び出し、DOMに反映させるための情報を読み込むことを指しています。呼び出すだけなのでDOMには反映しません。ではいつDOMに反映するのか?という疑問が出てくると思うのでプロセスを順を追って見ていきます。

Reactの画面表示プロセス

公式docにはこのように記載されています。

  1. Triggering a render
  2. Rendering the component
  3. Committing to the DOM

まず最初にレンダーをトリガーして、実際にコンポーネントをレンダー。そして最後にDOMにコミットという行為をするようです。カタカナが多くわかりづらいかもしれませんが、これだけでもDOMへの反映とレンダリングが異なることは分かりますね。

それでは1つずつそれぞれのステップを見ていきましょう。

STEP1: Trigger a render

レンダリングするきっかけをキャッチすることだと解釈しました。トリガーとなる出来事は2種類あります。1つ目は初期レンダリング。2つ目は画面更新時に発生する再レンダリングですね。

何も情報がなければReactはどのコンポーネントを描画すれば良いのか判断できません。そのため、最初のステップとしてまずレンダリングの引き金が必要なことは理解できると思います。

STEP2: Rendering the component

はい。ここがレンダリングという言葉をややこしくしているポイントです。先程も言った通り、ここではDOMへ反映させることは行っていません。公式の解説を読んでみましょう。

After you trigger a render, React calls your components to figure out what to display on screen.

レンダリングとはつまり計算を指しています。何を計算しているかというとレンダリング間の差分つまりdiffがあるかどうかの調査を行なっています。git diffのDOM版のようなイメージを持つと後続ステップでコミットしている理由が理解しやすいかもしれません。

再レンダリング時はそのトリガーした関数コンポーネントを呼び出しますが、初期レンダリングにそんなものはないのでルートコンポーネントを呼び出しています。

STEP:3 Committing to the DOM

After rendering (calling) your components, React will modify the DOM.

そしてDOMに反映されます。ここで最も重要なことが書いてあります。

React only changes the DOM nodes if there’s a difference between renders.

Reactは、レンダリング間に違いがある場合にのみDOMノードを変更します。
レンダリング間というのは前回レンダリングでの計算結果と今回のレンダリングでの計算結果だと自分は解釈しています。つまりこの両者の差分があった時のみDOMを変更しています。ここにきてやっと画面に直接関わるDOMへの反映行為を行なっています。レンダリングとコミットが混ざってしまっているために混乱が生まれていることがわかってきましたね。

diffが生まれたらその差分をcommitする。commitという単語はgitで使用するのでそのイメージがついていますが同じ様に捉えていいのではないかと筆者は思っています。公式ではレストランを例に取っているのでそちらの図を見てみるとより分かりやすいかも知れません。

Reactのプロセスは終わりましたがここまできてやっとブラウザが変更されたDOMを画面に反映します。これがいわゆるブラウザレンダリングという行為になりますね。

あらためてレンダリングとは?

Reactの画面表示プロセスを理解したところであらためてレンダリングが何を指すのか考えて見ましょう。

レンダリングって画面にDOMを反映することですよね?と思ったそこのあなた。
実は違います。なぜなら レンダリングが必ずしもDOMへの反映に繋がる わけではないからです。

まずはこちらの意味は分かってきたでしょうか?たしかにレンダリングはDOMへの反映工程の1部分ではあります。しかしレンダリングした結果、差分がなければコミットは行わない。つまりDOMへの反映が起こらないことがあるのです。

そのため、 「レンダリング=commitするかしないかの是非を判断するため、diffを計算すること」 だと私は考えています。DOM反映の準備工程を指しているのです。

なぜこのような齟齬が生まれたのか?

冒頭にお話したように render という動詞が、Reactが行うレンダリングとブラウザレンダリング両者の意味を含有している点にあります。docmentでもこの様に言っています。

After rendering is done and React updated the DOM, the browser will repaint the screen. Although this process is known as “browser rendering”, we’ll refer to it as “painting” to avoid confusion in the rest of these docs.

「ブラウザレンダリングと混ざるからブラウザレンダリングの方はpaintingという言葉で表します」と言っていますね。このように混乱を避けるため別の言葉を定義しています。

たしかにブラウザにレンダリングすること自体はブラウザがやることなので、Reactがブラウザレンダリングまではできないですよね。なぜReactは紛らわしい言葉を被せてきたのか、、、

また、それに加えDOMへの反映行為がブラウザレンダリングと混同している点も、区別しづらい原因であると思っています。DOMへの反映まではReactが行い、そのDOMをもとに描画を行うのがブラウザレンダリングのお仕事です。私自身この辺りの区別があまりついていませんでした。

レンダリングという言葉が指す意味に気をつけよう

これからReact開発において「レンダリング」がReactのプロセスを指しているのか、はたまたブラウザレンダリングのことを言っているのか常日頃から気をつける意識を持った方がいいかもしれません。

paintingという言葉の様に新しく言葉を定義して両者を区別できる様にしても良いかもしれません。

まとめ

  • Reactのレンダリングは必ずしもDOMへの反映と同義ではない
  • DOMへの反映と画面の描画も同義ではない
  • ReactのレンダリングはDOMのdiffを計算している
  • 画面の描画に直接関係しているのはcommitプロセス
  • 「レンダリング」の指す意味に気をつけよう

参考文献

https://zenn.dev/yodaka/articles/7c3dca006eba7d

https://qiita.com/hellokenta/items/6b795501a0a8921bb6b5

https://de-milestones.com/react-mount-rendering/

公式

https://reactjs.org/docs/strict-mode.html

  • beta

https://beta.reactjs.org/learn/render-and-commit#epilogue-browser-paint

Discussion