再レンダリングとは?
Reactでは、画面に表示する内容を更新するために、コンポーネントをもう一度実行して表示内容を計算し直すことがあります。
これを再レンダリングといいます。
ここで少し注意したいのが、再レンダリングではreturnの部分だけが実行されるわけではないということです。
たとえば、次のコードをApp.jsxファイルに記述してみましょう。
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const message = `現在の値は${count}です`;
console.log("Appが実行されました");
return (
<div>
<p>{message}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
}
export default App;
再レンダリングされると、ReactはAppコンポーネントをもう一度呼び出します。
そのため、messageの作成やconsole.log()も含めて、コンポーネント関数の中が上からもう一度実行されます。
開発者ツールで確認してみよう
先ほどのコードは、ブラウザの開発者ツールを使うと実際の動きを確認できます。
- ブラウザでアプリを開く
- 開発者ツールを開き、
Consoleタブを選ぶ
Appが実行されましたと表示されることを確認する
+1ボタンを押す
- もう一度
Appが実行されましたと表示されることを確認する
console.log()はreturnより前に書かれています。
それでもボタンを押したあとにもう一度ログが表示されることから、再レンダリングではreturnだけではなく、コンポーネント関数そのものが再び実行されていることを確認できます。
ただし、再レンダリングされたからといって、ブラウザのDOMをすべて作り直すわけではありません。
Reactは新しく計算した結果と前回の結果を比べ、必要な変更だけをDOMへ反映します。
再レンダリングされる主な条件
Reactコンポーネントが再レンダリングされる主なきっかけは、次の3つです。
- そのコンポーネントのStateが更新されたとき
- 親コンポーネントが再レンダリングされたとき
- そのコンポーネントが利用しているContextの値が変わったとき
ここからは、この3つの条件を順番に見ていきます。
① Stateが更新されたとき
まず、もっとも分かりやすいのがStateの更新です。
Stateを更新すると、Reactはそのコンポーネントの再レンダリングを要求します。
たとえば、次のカウンターを見てみましょう。
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
console.log("Appがレンダリングされました");
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
}
export default App;
ボタンを押すとsetCount()によってcountが更新されます。
するとReactはAppコンポーネントをもう一度実行し、新しいcountを使って表示内容を計算し直します。
そのため、ボタンを押すたびにコンソールへAppがレンダリングされましたと表示されます。
② 親コンポーネントが再レンダリングされたとき
自分自身のStateを持っていないコンポーネントでも、再レンダリングされることがあります。
通常、親コンポーネントが再レンダリングされると、その中で使われている子コンポーネントも再レンダリングされます。
次のコードをApp.jsxファイルに記述します。
この例では、AppコンポーネントのStateだけを更新します。
import { useState } from "react";
function Child() {
console.log("Childがレンダリングされました");
return <p>子コンポーネントです</p>;
}
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
<Child />
</div>
);
}
export default App;
countはAppコンポーネントのStateなので、更新されるとAppコンポーネントが再レンダリングされます。
そのとき、Appコンポーネントが返すJSXの中にあるChildコンポーネントも、通常は再レンダリングされます。
Childコンポーネント自身のStateが変わっていなくても、親コンポーネントの再レンダリングによって再び実行される点がポイントです。
propsが変わるときはどう考える?
Reactの再レンダリングについて調べると、「propsが変わると再レンダリングされる」と説明されることがあります。
結果だけを見ると間違いではありませんが、仕組みを理解するには、親コンポーネントとの関係まで見ると分かりやすくなります。
次のコードも、そのままApp.jsxファイルに記述して試せます。
親のAppコンポーネントから子のMessageコンポーネントへ、countをpropsとして渡しています。
import { useState } from "react";
function Message({ count }) {
return <p>現在の値は{count}です</p>;
}
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
+1
</button>
<Message count={count} />
</div>
);
}
export default App;
setCount()でStateが更新されると、まずAppコンポーネントが再レンダリングされます。
その結果、Messageコンポーネントも再レンダリングされ、新しいcountがpropsとして渡されます。
つまり、propsが自分で変更を検知して再レンダリングを起こしているわけではありません。
親が再レンダリングされ、その結果として子が新しいpropsを受け取ると考えると、Stateとpropsの関係を整理しやすくなります。
③ Contextの値が変わったとき
3つ目は、コンポーネントが利用しているContextの値が変わったときです。
Contextは、propsを何階層も渡していかなくても、離れたコンポーネントへ値を共有できるReactの仕組みです。
ContextをuseContext()で読み取っているコンポーネントは、そのContextの値が変わると再レンダリングされます。
たとえば、次のようにテーマをContextから受け取っているとします。
const theme = useContext(ThemeContext);
ThemeContextから提供される値が"light"から"dark"へ変わると、このContextを利用しているコンポーネントは新しい値で再レンダリングされます。
再レンダリングとDOM更新は別の処理
再レンダリングされると、Reactはコンポーネントをもう一度実行し、表示内容を計算し直します。
ただし、その計算結果をそのままDOM全体へ書き直すわけではありません。
- 再レンダリングのきっかけが発生する
- Reactがコンポーネントをもう一度実行する
- 新しい表示内容を計算する
- 前回との違いを確認し、必要な部分だけDOMへ反映する
そのため、コンポーネントが再レンダリングされても、計算結果が前回と同じならDOMが変更されないこともあります。
再レンダリングとDOM更新の違い
| 処理 |
何をする? |
| 再レンダリング |
コンポーネントを実行し、表示内容を計算し直す |
| DOM更新 |
計算結果に違いがある部分を実際のDOMへ反映する |
よくある勘違い
returnの部分だけがもう一度実行される?
returnの部分だけではありません。
関数コンポーネントそのものがもう一度呼び出されるため、returnより前に書かれた処理も再び実行されます。
再レンダリングするとページ全体が再読み込みされる?
されません。
再レンダリングはReactがコンポーネントの表示内容を計算し直す処理であり、ブラウザのページ再読み込みとは別のものです。
再レンダリングするとDOMを全部作り直す?
すべてを作り直すわけではありません。
Reactは再レンダリング後の結果と前回の結果をもとに、必要な変更だけをDOMへ反映します。
propsが変わったことを子コンポーネントが監視している?
そのように考える必要はありません。
通常は、親コンポーネントが再レンダリングされ、その中で子コンポーネントも再レンダリングされることで、新しいpropsが使われます。
普通の変数を変更しても再レンダリングされる?
通常のJavaScript変数を書き換えただけでは、Reactに再レンダリングを要求することにはなりません。
画面表示に使い、変更後に再レンダリングしたい値は、Stateとして管理することを検討します。
まとめ
Reactの再レンダリングでは、関数コンポーネントそのものがもう一度実行され、表示内容が計算し直されます。
returnの部分だけが実行されるわけではありません。
Reactコンポーネントが再レンダリングされる主なきっかけは、Stateの更新、親コンポーネントの再レンダリング、利用しているContextの値の変更の3つです。
- Stateを更新すると、そのコンポーネントの再レンダリングが要求される
- 親コンポーネントが再レンダリングされると、子コンポーネントも通常は再レンダリングされる
- propsは親の再レンダリングによって新しい値が子へ渡される、と考えると分かりやすい
- 利用しているContextの値が変わると、そのコンポーネントが再レンダリングされる
- 再レンダリングとDOM更新は同じものではない
再レンダリングは、コンポーネントをもう一度実行して表示内容を計算し直す処理であり、DOMをすべて作り直す処理ではありません。