コンポーネントが画面に表示される前に、React によってレンダリングされる必要があります。このプロセスの手順を理解すると、コードがどのように実行されるかを考えるのに役立ち、その動作を説明できるようになります。

学習内容

  • React におけるレンダリングの意味
  • React がコンポーネントをレンダリングするタイミングとその理由
  • コンポーネントを画面に表示する際の手順
  • レンダリングが常に DOM 更新を生成するとは限らない理由

コンポーネントは、食材から美味しい料理を組み立てる厨房の料理人だと考えてください。このシナリオでは、React はお客様からのリクエストを受け取り、注文を届けるウェイターです。UI をリクエストして提供するこのプロセスには、3 つの手順があります。

  1. レンダーをトリガーする (お客様の注文を厨房に届ける)
  2. コンポーネントをレンダリングする (厨房で注文を準備する)
  3. DOM にコミットする (テーブルに注文を置く)
  1. React as a server in a restaurant, fetching orders from the users and delivering them to the Component Kitchen.
    トリガー
  2. The Card Chef gives React a fresh Card component.
    レンダー
  3. React delivers the Card to the user at their table.
    コミット

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

コンポーネントをレンダリングする理由は 2 つあります。

  1. コンポーネントの初期レンダリングである。
  2. コンポーネント (またはその祖先のいずれか) のステートが更新された

初期レンダリング

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

import Image from './Image.js';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'))
root.render(<Image />);

root.render() 呼び出しをコメントアウトして、コンポーネントが消えるのを確認してください!

ステートが更新されたときの再レンダリング

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

  1. React as a server in a restaurant, serving a Card UI to the user, represented as a patron with a cursor for their head. They patron expresses they want a pink card, not a black one!
    ステート更新...
  2. React returns to the Component Kitchen and tells the Card Chef they need a pink Card.
    ...トリガー...
  3. The Card Chef gives React the pink Card.
    ...レンダー!

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

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

  • 最初のレンダーでは、React はルートコンポーネントを呼び出します。
  • 後続のレンダーでは、React は、状態の更新がレンダーをトリガーした関数コンポーネントを呼び出します。

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

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

export default function Gallery() {
  return (
    <section>
      <h1>Inspiring Sculptures</h1>
      <Image />
      <Image />
      <Image />
    </section>
  );
}

function Image() {
  return (
    <img
      src="https://i.imgur.com/ZF6s192.jpg"
      alt="'Floralis Genérica' by Eduardo Catalano: a gigantic metallic flower sculpture with reflective petals"
    />
  );
}

  • 最初のレンダー中、React は DOM ノードを作成 して、<section><h1>、および 3 つの <img> タグを作成します。
  • 再レンダー中、React は、前のレンダーから変更されたプロパティ(もしあれば)を計算します。この情報は、次のステップであるコミットフェーズまで何もしません。

落とし穴

レンダーは常に純粋な計算である必要があります

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

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

深掘り

パフォーマンスの最適化

更新されたコンポーネント内にネストされたすべてのコンポーネントをレンダーするデフォルトの動作は、更新されたコンポーネントがツリー内で非常に高い位置にある場合、パフォーマンスにとって最適ではありません。パフォーマンスの問題が発生した場合は、パフォーマンスセクションで説明されているように、いくつかのオプトイン方法で解決できます。時期尚早な最適化は避けてください!

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

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

  • 最初のレンダーの場合、React は appendChild() DOM API を使用して、作成したすべての DOM ノードを画面に表示します。
  • 再レンダーの場合、React は、DOM を最新のレンダー出力に一致させるために必要な最小限の操作を(レンダー中に計算された!)適用します。

React は、レンダー間に違いがある場合にのみ DOM ノードを変更します。たとえば、これは親から毎秒異なるプロパティを渡して再レンダーするコンポーネントです。<input> にテキストを追加して value を更新できますが、コンポーネントが再レンダーされるときにテキストが消えないことに注目してください。

export default function Clock({ time }) {
  return (
    <>
      <h1>{time}</h1>
      <input />
    </>
  );
}

これは、この最後のステップ中に、React が新しい time<h1> の内容のみを更新するためです。JSX 内で <input> が前回と同じ場所にあることを認識するため、React は <input> やその value に触れません。

エピローグ: ブラウザペイント

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

まとめ

  • React アプリの画面更新は、次の 3 つのステップで発生します
    1. トリガー
    2. レンダー
    3. コミット
  • 厳格モードを使用してコンポーネントの間違いを見つけることができます
  • Reactは、レンダリング結果が前回と同じ場合、DOMを操作しません。