再レンダリングの仕組み

3 つの段階

ボタンを押してから画面が変わるまで、React は毎回同じ 3 つの段階を通ります。

  1. きっかけ ... 更新関数が呼ばれ、React に「state が変わった」と伝わる
  2. レンダリング ... React がそのコンポーネント関数をもう一度呼び、返ってきた JSX で新しい画面の設計図を作る
  3. 反映 ... 前の設計図と見くらべ、違うところだけを実際の画面に書き込む

大事なのは 2 番です。React は画面を部分的に手直ししているのではなく、関数をまるごと呼び直して、新しい結果と前の結果を見くらべて います。

呼び直されているのを目で見る

コンポーネントの中に console.log を置くと、いつ呼ばれているかが分かります。

import { useState } from "react"; export default function App() { const [likes, setLikes] = useState(0); console.log("App がレンダリングされました", likes); return <button onClick={() => setLikes(likes + 1)}>いいね {likes}</button>; }

押すたびにログが出ます。関数の先頭から毎回やり直しているので、const [likes, setLikes] = useState(0) の行も毎回通っています。それでも 0 に戻らないのは、値を覚えているのが React 側だからです。

反映されるのは違うところだけ

3 番のおかげで、画面全体が作り直されるわけではありません。見出しの文字が変わっていなければ、その部分の DOM はそのまま残ります。だから入力途中の文字やスクロール位置が消えません。「関数は毎回呼ばれるが、書き込まれるのは差分だけ」と覚えておいてください。

同じ値を渡すと何も起きない

更新関数に 今と同じ値 を渡した場合、React は変化なしと判断してレンダリングを省くことがあります。

setLikes(likes); // 何も起きない

一方、別の state を更新すればコンポーネントは呼び直されるので、そのついでに likes の表示も作り直されます。ただし値は同じなので、見た目は変わりません。

更新はまとめて処理される

1 回のクリックの中で更新関数を何度呼んでも、レンダリングは 1 回にまとめられます。

function handleClick() { setLikes(likes + 1); setClicks(clicks + 1); }

途中の中途半端な画面は出ません。2 つの state がそろって新しくなった状態で 1 回だけ描き直されます。これをバッチ処理と呼びます。

つまずきどころ

「関数が毎回呼ばれる」と知ると、重い計算をコンポーネントの中に書いてよいのか不安になります。今の段階では気にしなくて大丈夫です。表示のための軽い計算なら毎回やっても問題になりません。本当に重い処理をどう避けるかは、もっと先の話題です。

もう 1 つ、レンダリングの途中で更新関数を呼んではいけません。return の前に直接 setLikes(...) と書くと、レンダリングがまたレンダリングを呼び、止まらなくなります。更新関数を呼ぶのはイベントの中だけです。

演習

同じ値を渡したときと違う値を渡したときで、画面がどう変わるかを確かめます。

ヒント

App.jsx
App.jsx
プレビュー

できているか

  • はじめは「操作回数 0」と表示されている
  • いいねを増やすボタンを押すと、いいね数も操作回数も 1 になる
  • 続けて「同じ値をセット」を 2 回押すと、いいね数は 1 のままで操作回数だけ 3 になる