🍭

【初心者向け】Reactのレンダリングについて学んでみよう!

2024/09/12に公開

はじめに

Reactのレンダリングそのものについての記事は意外と少ないと感じたため、執筆しました!
Reactのレンダリングメカニズムを理解することは、効率的な開発とパフォーマンス最適化に直結します。
この記事では、Reactのレンダリングの基本的な概念をお届けし、React開発をよりスムーズにするお手伝いができればと思っています!

問題

以下に、姓と名の入力フィールドで構成されたシンプルなコンポーネントがあります。
あなたは、このコンポーネントの姓のフィールドに「react」、名のフィールドに「test」と入力しました。
ここで、次の問いに答えてください。

  1. 合計で何回レンダリングが発生しますか?
  2. 要件として、入力フィールドに文字が入力された際、名前(fullName)をタイムリーに表示させる必要があります(例えば、「あ」と入力したら「あ」が即座に表示される)。
    以下のコンポーネントはその要件を満たしていますか?理由も含めて答えてください。
  3. firstName,lastNameが更新されるタイミングでconsole.log("life")を実行したいです。
    どのようにコードを追加・編集しますか?ただし、onChange に直接 console.log("life") を渡す方法は使わないでください。
import { useState } from "react";

function App() {
  const [lastName, setLastName] = useState<string>("");
  const [firstName, setFirstName] = useState<string>("");
  
  const fullName = lastName + firstName

  return (
    <>
      <input onChange={(e) => setLastName(e.target.value)} value={lastName} />
      <input onChange={(e) => setFirstName(e.target.value)} value={firstName} />
      {fullName}
    </>
  );
}

export default App;

Reactのレンダリングとは??

問題の解説を行う前に、Reactのレンダリングについて簡単にまとめます。
Reactのレンダリングについて理解していれば、上記の問題はすぐに解けるでしょう。

まず、一般的なレンダリング(ロードから画面描写まで)と Reactのレンダリングは同じではありません。
では、Reactのレンダリングとは何かというと、Reactがコンポーネントを実行すること です(公式には「レンダー」と表記されていますが、これは一般的なレンダリングと区別するために使われている用語です)。

Reactにおけるコンポーネントの「実行」とは、コンポーネント内のロジックを再計算することで、更新された後のstateを使って行われます。
また、コンポーネントに子コンポーネントがあれば、それらも一緒に再計算されます。

レンダリング時、基本的にコンポーネント内のJSX・関数・変数は再計算されますが、例外もあります。

  • memo化されたコンポーネント・関数・変数
    • propsや依存配列が変わらない限り。
  • イベントハンドラ
  • useEffect

これらはレンダリングサイクルの中では再計算されません。

また、Reactにとってレンダリングとは単に「コンポーネントを実行すること」であり、画面描写は全く別のプロセスです。
実際、公式には以下のように記載されています。

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

レンダリングを実行した際に以前の状態との差分がなければ、画面描写は発生しません。
つまり、レンダリングを実行しても、UIに変化が見られないことがあります。

レンダリングはいつ実行される??

Reactのレンダリングはいつ実行されるのか、公式には以下のように記載されています。

コンポーネントがレンダーされる理由には 2 つあります。
1.コンポーネントの初回レンダー。
2.コンポーネント(またはその祖先のいずれか)の state の更新。

さらに親がレンダリングされたら子もレンダリングされるので、以下のタイミングでレンダリングは実行されます。

  1. 初回レンダリング
  2. stateの更新
  3. 親コンポーネントのレンダリング

親コンポーネントのレンダリングは結局のところ、初回レンダリングもしくはstateの更新によって行われます。
つまり、stateuseStateuseContextなど)がどこで宣言され、どのタイミングでstateが更新される(set関数の実行)かを把握することで、レンダリングがいつ実行されるのかを理解することができます!!

ちなみにpropsが変更されたらレンダリングされるというのはあまり正しくないです。
なぜなら、propsを変更するには結局のところ親コンポーネントをレンダリングする必要があるからです。
さらに、memo化をしていない場合、propsの変更の有無に関わらず、レンダリングは実行されます。
そのため、memo化をしている場合にのみ、propsの変更がレンダリングに影響を与えることになります。

ここまでのまとめ

  • Reactにおけるレンダリングとは「コンポーネントを実行すること」です。
  • レンダリングが実行されるタイミング(レンダリングのトリガー)は以下の通りです:
    • 初回レンダリング
    • stateの更新
    • 親コンポーネントのレンダリング

問題の答えと解説

1.合計で何回レンダリングが発生しますか?

答え:9回(初回を含めると10回)

解説:onChangeイベントが発生すると、stateを更新する(setLastNameまたはsetFirstNameが実行される)仕様になっています。
stateを更新することはレンダリングのトリガーになるため、文字数分レンダリングが発生します。

  • rを入力 → onChangeイベントが発生 → stateを更新 → レンダリングが発生 → ペイント
  • eを入力 → onChangeイベントが発生 → stateを更新 → レンダリングが発生 → ペイント
  • aを入力 → ...

2.要件として、入力フィールドに文字が入力された際、名前(fullName)をタイムリーに表示させる必要があります(例えば、「あ」と入力したら「あ」が即座に表示される)。
以下のコンポーネントはその要件を満たしていますか?理由も含めて答えてください。

答え:文字が入力されるたびに fullName を再計算し、UIを更新(ペイント)するため、要件を満たしています。

解説:レンダリングが発生すると、更新されたstateをもとにコンポーネント内を再計算します。これにより、UIがタイムリーに更新されます。

  • rを入力→onChangeイベントが発生→stateを更新→レンダリングが発生→fullNameを再計算 →fullNameにaが代入される → ペイント → ブラウザにaが表示される
  • eを入力→onChangeイベントが発生→stateを更新→レンダリングが発生→fullNameを再計算 →fullNameにaiが代入される → ペイント → ブラウザにaiが表示される
  • aを入力...

3.firstName,lastNameが更新されるタイミングでconsole.log("life")を実行したいです。
どのようにコードを追加・編集しますか?ただし、onChange に直接 console.log("life") を渡す方法は使わないでください。

答え:コンポーネント内にconsole.log("life")を追加するだけ

import { useState } from "react";

function App() {
  const [lastName, setLastName] = useState<string>("");
  const [firstName, setFirstName] = useState<string>("");
  
  const fullName = lastName + firstName
  
  // これだけ
  console.log("life")

  return (
    <>
      <input onChange={(e) => setLastName(e.target.value)} value={lastName} />
      <input onChange={(e) => setFirstName(e.target.value)} value={firstName} />
      {fullName}
    </>
  );
}

export default App;

解説:firstName,lastNameが更新される=レンダリングのトリガーであるため

最後に

レンダリングを理解することはReact開発において非常に重要です!!
私自身も、レンダリングのメカニズムを理解している時とそうでない時では、開発プロセスに大きな違いがありました。

レンダリングの仕組みを把握していると、パフォーマンスの最適化やコンポーネントの再レンダリングを効率的に管理できるようになります。

一方で、レンダリングの仕組みを理解していないと、無駄なレンダリングが頻繁に発生し、パフォーマンスが低下する原因となります。
また、デバッグが難しくなり、意図しないUIの挙動に悩まされることもあります。

さらに、レンダリングの実行中に何が行われているのかを深く理解したい方は、以下の記事も参考にしてみてください!
https://zenn.dev/aishift/articles/d046335a98bc34

レンダリングの仕組みをしっかり把握して、効率的でスムーズなReact開発ライフを楽しみましょう💫

参考

https://ja.react.dev/learn/render-and-commit
https://qiita.com/yokoto/items/ee3ed0b3ca905b9016d3

Discussion