「propsで違う値を渡しているはずなのにコンポーネントに更新が走らない」
「Reactのレンダリングがイマイチ理解できていない状態で開発している」
「mapでkeyを書き忘れると警告文が出ることがあるが、何のためにkeyが必要かがわからない」
本日はそんな方に向けてReactのレンダリングを理解してもらう内容になります。
Reactでコンポーネントがレンダリングされないという問題は初学者にとっては何回も遭遇することがあります。
この記事では、Reactアプリケーションでコンポーネントが正しく表示されない1例を紹介します。
またReactでコンポーネントがレンダリングされない場合、keyプロパティの使用方法を理解することで解決できるのでkeyの使い方も一緒に解説します。
動画でも解説しているので必要な方はご覧ください。
propsで違う値を渡しているはずなのにコンポーネントに更新が走らない?
まずは例題として以下のようなカウンターアプリを想定します。
import "./App.css";
import Render from "./screens/wrongIfRender/Render";
function App() {
return (
<div className="App">
<Render />
</div>
);
}
export default App;
import React from "react";
import { useState } from "react";
import Counter from "../wrongIfRender/Counter";
const Render = () => {
const [isAdmin, setAdmin] = useState(true);
return (
<div>
{isAdmin ? <Counter name="管理者" /> : <Counter name="スタッフ" />}
<br />
<button onClick={() => setAdmin((user) => !user)}>切り替え</button>
</div>
);
};
export default Render;
import React, { useState } from "react";
const Counter = ({ name }) => {
const [count, setCount] = useState(0);
return (
<div>
<h1>{name}</h1>
<button onClick={() => setCount((current) => current + 1)}>+</button>
<button onClick={() => setCount((current) => current - 1)}>-</button>
<p>現在の数字は{count}です</p>
</div>
);
};
export default Counter;

Reactを学習されている方なら1度は練習したことのあるシンプルなカウンターですね。
少し手を加えているのがRender.jsにてisAdminというステートを作っている点です。
こちらはtrue,falseの値を取って、管理者かスタッフかというユーザーの判定を行っていると思ってください。
「切り替え」というボタンをクリックすることでisAdminの値がtrueとfalseに交互に切り替わることで管理者とスタッフの表示が切り替わる形です。
例えば管理者の状態でボタンをクリックするとスタッフに切り替わり、またその逆もできます。

こちらのアプリですが動作させること自体はそこまで難しくないのですが1点良くない点があります。
カウントした結果が管理者とスタッフを切り替えても残ってしまう点です。
このような管理者とスタッフを切り替えるアプリではそれぞれ別で動作させることが多く、管理者とスタッフを切り替えるとカウントも1度リセットさせたいことがあるわけです。
カウントがリセットされないということは子コンポーネントであるCounter.jsではレンダリング(更新)が行われていないことを意味しています。
Reactを勉強し始めた時に「レンダリング」という言葉を何回も見聞きしたことでしょう。
しかし実際のところレンダリングを意識する意味がわかっていない方は多いはずです。
上記の例はレンダリングによって困るパターンあるあるですので一度は経験しておくと良いでしょう。
一方で少し理解されている方であれば逆に疑問に思うかもしれません。
「レンダリングは子コンポーネントに変更があった時に行われるはずなのに、ユーザーを切り替えてもレンダリングされないのはなぜ?」
とても良いところに目を付けれています。
おっしゃる通りです、レンダリングは画面をリロードせずとも子コンポーネントに変更があれば都度実行されます。
しかしよく考えてみて欲しいのですが、ユーザーの切り替えはCounter.jsから見た親コンポーネントであるRender.jsで監視していましたね。
そのためユーザーの切り替えだけではCounter.jsは「変更なし」と判断されてレンダリングされないわけです。
Reactのkeyは何のために書くのか?
それではどうするか?というお話です。
基本的には子コンポーネントに変更があればレンダリングされることには変わりないです。
しかし今回の場合は先ほどお話したようにユーザーは親コンポーネントであるRender.jsで管理しているため、ユーザーの切り替え以外で子コンポーネントであるCounter.jsに変更を加えねばなりません。
一番手取り早いのは以下のように表示する内容を変えることです。
import React from "react";
import { useState } from "react";
import Counter from "../wrongIfRender/Counter";
const Render = () => {
const [isAdmin, setAdmin] = useState(true);
return (
<div>
{/* ここを変更 */}
{isAdmin ? (
<div>
<Counter name="管理者" />
</div>
) : (
<section>
<Counter name="スタッフ" />
</section>
)}
<br />
<button onClick={() => setAdmin((user) => !user)}>切り替え</button>
</div>
);
};
export default Render;
Counter.jsを管理者、スタッフそれぞれで別のHTMLタグで囲んでみました。
画面上は特に変わりないですが、ユーザーによって違う表示をさせることになりますよね。
これで期待したような動作にすることができました。
まずはこのやり方を覚えておきましょう、レンダリングの有無がイメージしやすくなるはずです。
しかし実務では使いにくい方法と言われがちです。
そこでmapメソッドでも登場したkeyを使っても同じことができます。
import React from "react";
import { useState } from "react";
import Counter from "../wrongIfRender/Counter";
const Render = () => {
const [isAdmin, setAdmin] = useState(true);
return (
<div>
{/* ここを変更 */}
{isAdmin ? (
<Counter name="管理者" key="admin" />
) : (
<Counter name="スタッフ" key="staff" />
)}
<br />
<button onClick={() => setAdmin((user) => !user)}>切り替え</button>
</div>
);
};
export default Render;
全く同じ動作になっています。
mapメソッドを使うときにkeyを書いていないと警告文やコンソールエラーが表示された経験がある方は多いと思います。
学習教材やスクールでは「mapにはkeyが必要だから」くらいにしか解説されませんが、実は重要な役割があります。
コンポーネントにkeyで文字列などの値を書いておくことで、それが目印のような役割になって繰り返しの順番やコンポーネントの変化を読み取ってくれるようになります。
詳しくは公式ドキュメントを確認してみてください↓
https://ja.legacy.reactjs.org/docs/lists-and-keys.html#keys
今回の例題は「レンダリングさせたい」というケースでお話していますが、もし「レンダリングさせたくない」というケースではkeyを付けないようにしてくださいね。
「レンダリングの善悪」ではなく、それぞれの違いと対応方法をしっかり把握しておくことが一番大事です。