Reactの条件付きレンダリングとは、普通のJavaScriptでコンポーネントが何を返すかを選ぶことです。return の前の if 文、JSXの中の三項演算子 cond ? a : b、何かを表示するか何も表示しないかの cond && <Thing /> を使います。覚えるべき特別なテンプレート構文はありません。
ボタンをクリックすると、メッセージとラベルの両方が切り替わります。次のレンダリングで、それぞれの三項演算子が別の文字列を選ぶからです。useState(false) を useState(true) に変えると、プレビューはログインした状態で始まります。
if文と早期return
JSXの波かっこが受け付けるのは文ではなく式なので、マークアップの中に {if (x) ...} とは書けません。return の前なら好きなだけ if を書けます。コンポーネントの状態全体の見た目が違うとき(読み込み中、エラー、空)は、それぞれの場合で早めにreturnし、メインのマークアップは通常の場合のために残しておきます。
各ボタンは受信箱を別々の状態にし、それぞれの状態に短くて読みやすい分岐があります。早期returnを使えば、ネストせずに正常系を一番下に置けます。
JSXの中の三項演算子
マークアップのごく一部だけが変わるなら、インラインの三項演算子でまとめておけます:{isOpen ? <CloseIcon /> : <MenuIcon />}。三項演算子は選択肢が2つのときに便利です。3つ以上でネストした三項演算子は、すぐに読みにくくなります。
// Hard to follow
{status === 'loading' ? <Spinner /> : status === 'error' ? <ErrorBox /> : <List />}
そうなったら、ロジックを return の前の if の連鎖(上の例のように)か、オブジェクトのマップ(後述)に移してください。
&&と余計な0
cond && <Thing /> は、cond が真のときに <Thing /> を描画し、false のときは何も描画しません。これが動くのは、&& が左側が偽のときに左側を返し、Reactが false、null、undefined を何も描画しないからです。
落とし穴は、0 も偽であり、Reactは数値を描画するという点です。そのため、カウントが 0 のときの count && <Badge /> はページに 0 という文字をそのまま置きます。
未読メッセージがないと、1行目は「Broken: 0」と表示され、2行目は「Fixed:」の後に何も表示されません。New messageをクリックすると両方が一致し、Mark all readをクリックすると 0 が戻ってきます。解決策は && に本物の真偽値を渡すことです:unread > 0、items.length > 0、Boolean(unread)。空文字列 '' も理屈では同じ問題がありますが、目に見える形では何も描画されないので、気づくことはまれです。
nullを返す
コンポーネントは null を返して何も描画しないことができます。警告すべきことがあるときだけ現れる警告バナーがよい例です。親は常に <Warning /> を描画し、表示するかどうかはコンポーネントが決めます。
コンソールを見てください。Warning 関数は、null を返すときも含め、レンダリングのたびにログを出します。null を返して省かれるのは出力であって、処理ではありません。フックやエフェクトは引き続き動くので、コンポーネントが重かったり何か(タイマー、購読)を始めたりするなら、親で判断してそもそも描画しないようにしてください。
JSXを変数に入れる
JSXは値なので、変数に代入し、if 文で組み立てて、後でマークアップに入れられます。大きなレイアウトの一部が複数の条件に依存するときに役立ちます。
function Checkout({ cart, user }) {
let action;
if (!user) {
action = <a href="/login">Log in to check out</a>;
} else if (cart.length === 0) {
action = <p>Your cart is empty.</p>;
} else {
action = <button>Pay now</button>;
}
return (
<section>
<h2>Your cart</h2>
{action}
</section>
);
}
オブジェクトで複数のコンポーネントから選ぶ
「多くの中から1つ」を選ぶときは、各値をコンポーネントに対応させるオブジェクトのほうが、長い if の連鎖や switch より短く、ケースの追加も1行で済みます。
TABS[tab] がコンポーネントを選び、<Panel /> がそれを描画します。変数は大文字で始める必要があります。JSXは <panel /> をHTMLタグとして扱うからです。独自のコンポーネントを持つ notifications の項目を TABS に追加すると、4つ目のボタンが現れます。
隠すことと描画しないことは違う
何かを消す方法は2つあり、振る舞いが違います。show && <Counter /> は、show がfalseのときにコンポーネントをツリーから取り除きます。Reactはそれをアンマウントしてstateを捨てるので、戻ってきたときは最初からになります。CSSで隠す(style={{ display: show ? 'block' : 'none' }})とマウントされたままなので、stateは残ります。
両方のカウンターを何回かクリックし、チェックボックスを外してから、もう一度チェックしてください。1つ目のカウンターは0に戻り、2つ目は数値を保っています。どちらも間違いではありません。毎回空の状態で始めるべきフォームには取り除くのが正しく、スクロール位置や入力を保つべきタブには隠すのが正しいのです。Reactがレンダリング間でコンポーネントを同じものと判断する仕組みは、レンダリングのページで説明しています。
React 19.2では3つ目の選択肢 <Activity mode="hidden"> が追加されました。CSSと同じように子を隠してstateを保ち、さらに再び表示されるまでその子のエフェクトをクリーンアップします。
条件付きのpropsと属性
条件はどの要素が現れるかだけの話ではありません。どのpropも計算した値を受け取れるので、属性の中の三項演算子や比較だけで済むことがよくあります。
<button disabled={!canSave} className={hasError ? 'field error' : 'field'}>
Save
</button>
<input aria-invalid={hasError} placeholder={user ? user.email : 'you@example.com'} />
propの値が undefined のとき、Reactはその属性を完全に省きます。ときどきだけ属性を付けたいときのすっきりした方法です:title={isTruncated ? fullText : undefined}。disabled や checked のような真偽値の属性には、true か false を渡すと、Reactが追加したり取り除いたりします。
リストの中の条件
リストから項目を除きたいときは、map の中から null を返すのではなく、map の前に配列をフィルターしてください。items.filter((i) => i.done).map(...) は書いてあるとおりに読めます。リストとkeyのページで、動く例とともに紹介しています。
よくある質問
JSXの中でif文はどう書きますか?
JSXの波かっこは式を受け付けるものなので、その中に if 文は書けません。JSXの中では三項演算子(cond ? a : b)や && を使うか、return の前で if を実行してJSXを変数に入れてください。
コンポーネントに0が表示されるのはなぜですか?
count && <Badge /> は count が 0 のとき count そのものを返し、Reactは数値の 0 を描画します。左側を本物の真偽値にしてください:count > 0 && <Badge />。
Reactで何も描画しないにはどうしますか?
コンポーネントから null を返すか、JSXの中に null、undefined、false、true を置きます。Reactはどれも描画しません。
nullを返すコンポーネントも実行されますか?
はい。関数は実行され、フックも動き、エフェクトも発火します。null を返すのは、ページに何も追加しないという意味だけです。処理そのものを省きたいなら、親からそのコンポーネントを描画しないでください。
コンポーネントはCSSで隠すべきですか、取り除くべきですか?
取り除く(show && <Panel />)とアンマウントされるので、stateは捨てられ、次は最初からになります。display: none で隠すとマウントされたままで、stateも保たれます。stateを残すべきかどうかで選んでください。