v19.2Latest

レンダーとコミット

コンポーネントが画面に表示される前に、Reactによってレンダーされる必要があります。このプロセスのステップを理解することで、コードの実行方法について考え、その動作を説明できるようになります。

学習内容
  • Reactにおけるレンダリングの意味
  • Reactがコンポーネントをレンダーするタイミングと理由
  • コンポーネントを画面に表示するまでのステップ
  • レンダリングが常にDOMの更新を生じない理由

コンポーネントを厨房で材料から美味しい料理を組み立てる料理人、Reactを客からの注文を受け取り料理を運ぶウェイターと想像してください。このUIの要求と提供のプロセスには3つのステップがあります:

  1. トリガー(客の注文を厨房に届ける)
  2. レンダリング(厨房で注文を準備する)
  3. コミット(注文をテーブルに置く)
  1. レストランのサーバーとしてのReactがユーザーから注文を取得し、コンポーネントキッチンに届けます。トリガー
  2. カードシェフがReactに新しいCardコンポーネントを渡します。レンダー
  3. ReactがユーザーのテーブルにCardを届けます。コミット

イラスト提供:Rachel Lee Nabors

ステップ1: レンダーをトリガーする

コンポーネントがレンダーされる理由は2つあります:

  1. コンポーネントの初回レンダー
  2. コンポーネント(またはその祖先のいずれか)の状態が更新された

初回レンダー

アプリが起動すると、初回レンダーをトリガーする必要があります。フレームワークやサンドボックスではこのコードが隠されていることもありますが、ターゲットDOMノードを指定してcreateRootを呼び出し、そのrenderメソッドにコンポーネントを渡して呼び出すことで行われます:

root.render()の呼び出しをコメントアウトしてみると、コンポーネントが消えるのがわかります!

状態更新時の再レンダー

コンポーネントが初回レンダーされた後は、set関数で状態を更新することで、さらにレンダーをトリガーできます。コンポーネントの状態を更新すると、自動的にレンダーがキューに入ります。(これは、レストランの客が最初の注文をした後、喉の渇きや空腹の状態に応じて、紅茶やデザート、様々なものを注文する様子と想像できます。)

  1. レストランのサーバーとしてのReactが、カーソルを頭にした客として表現されたユーザーにCard UIを提供しています。客は黒いカードではなくピンクのカードが欲しいと表明しています!状態更新...
  2. Reactがコンポーネントキッチンに戻り、カードシェフにピンクのCardが必要だと伝えます。...がトリガー...
  3. カードシェフがReactにピンクのCardを渡します。...レンダー!

イラスト提供:Rachel Lee Nabors

ステップ2: Reactがコンポーネントをレンダーする

レンダーをトリガーした後、Reactは画面に何を表示するかを決定するためにコンポーネントを呼び出します。「レンダリング」とは、Reactがコンポーネントを呼び出すことです。

  • 初回レンダーでは、Reactはルートコンポーネントを呼び出します。
  • それ以降のレンダーでは、Reactは状態更新によってレンダーがトリガーされた関数コンポーネントを呼び出します。

このプロセスは再帰的です:更新されたコンポーネントが他のコンポーネントを返す場合、Reactは次にそのコンポーネントをレンダーし、そのコンポーネントも何かを返す場合は、次にそのコンポーネントをレンダーする、といった具合です。ネストされたコンポーネントがなくなり、Reactが画面に表示すべきものを正確に把握するまで、このプロセスは続きます。

次の例では、ReactはGallery()Image()を複数回呼び出します:

  • 初回レンダリング時、ReactはDOMノードを作成します。対象は<section><h1>、そして3つの<img>タグです。
  • 再レンダリング時、Reactは前回のレンダリング以降にどのプロパティが変更されたかを計算します。この情報は次のステップであるコミットフェーズまで何も行われません。
落とし穴

レンダリングは常に純粋な計算でなければなりません:

  • 同じ入力、同じ出力。同じ入力が与えられた場合、コンポーネントは常に同じJSXを返すべきです。(誰かがトマト入りのサラダを注文したら、玉ねぎ入りのサラダを受け取るべきではありません!)
  • 自分のことだけを考える。レンダリング前に存在していたオブジェクトや変数を変更してはいけません。(ある注文が他の誰かの注文を変更するべきではありません。)

そうでなければ、コードベースが複雑になるにつれて、混乱を招くバグや予測不可能な動作に遭遇する可能性があります。「Strict Mode」で開発している場合、Reactは各コンポーネントの関数を2回呼び出します。これは、純粋でない関数によって引き起こされる間違いを表面化させるのに役立ちます。

Deep Dive
パフォーマンスの最適化

ステップ3: ReactがDOMに変更をコミットする

コンポーネントをレンダリング(呼び出し)した後、ReactはDOMを変更します。

  • 初回レンダリングでは、ReactはappendChild()DOM APIを使用して、作成したすべてのDOMノードを画面に配置します。
  • 再レンダリングでは、ReactはDOMが最新のレンダリング出力と一致するように、最小限の必要な操作(レンダリング中に計算されたもの!)を適用します。

Reactは、レンダリング間に差異がある場合にのみDOMノードを変更します。例えば、ここに親コンポーネントから渡される異なるpropsで毎秒再レンダリングされるコンポーネントがあります。<input>にテキストを追加してそのvalueを更新しても、コンポーネントが再レンダリングされてもテキストが消えないことに注目してください:

これは、この最後のステップで、Reactが新しい<h1>の内容をtimeで更新するだけだからです。Reactは<input>が前回と同じ場所にJSX内に現れることを確認するので、Reactは<input>、またはそのvalueに触れません!

エピローグ: ブラウザの描画

レンダリングが完了し、ReactがDOMを更新した後、ブラウザは画面を再描画します。このプロセスは「ブラウザレンダリング」として知られていますが、ドキュメント全体で混乱を避けるために「描画」と呼びます。

ブラウザが「カード要素のある静物画」を描画している様子。

イラスト:Rachel Lee Nabors

まとめ

  • Reactアプリケーションにおける画面更新は、3つのステップで行われます:
    1. トリガー
    2. レンダリング
    3. コミット
  • Strict Modeを使用してコンポーネント内の間違いを見つけることができます
  • レンダリング結果が前回と同じ場合、ReactはDOMに触れません