条件付きレンダリング

コンポーネントは、多くの場合、異なる条件に応じて異なるものを表示する必要があります。Reactでは、if文、&&、および? :演算子のようなJavaScript構文を使用して、JSXを条件付きでレンダリングできます。

学習内容

  • 条件に応じて異なるJSXを返す方法
  • JSXの一部を条件付きで含めたり除外したりする方法
  • Reactコードベースで遭遇する一般的な条件付き構文のショートカット

JSXの条件付き返し

いくつかのItemをレンダリングするPackingListコンポーネントがあるとします。これらのアイテムは、梱包済みとしてマークすることもできます。

function Item({ name, isPacked }) {
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

いくつかのItemコンポーネントのisPackedプロパティがfalseではなくtrueに設定されていることに注意してください。isPacked={true}の場合、梱包済みのアイテムにチェックマーク(✅)を追加したいと考えています。

これをif/elseとして記述することができます。

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

isPackedプロパティがtrueの場合、このコードは異なるJSXツリーを返します。 この変更により、一部のアイテムの最後にチェックマークが表示されます。

function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name}</li>;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

どちらの場合にも返されるものを編集して、結果がどのように変わるかを確認してみてください!

JavaScriptのif文とreturn文を使って分岐ロジックを作成していることに注目してください。Reactでは、制御フロー(条件など)はJavaScriptによって処理されます。

nullによる条件付きの何も返さない

状況によっては、何もレンダリングしたくない場合もあります。たとえば、梱包済みのアイテムをまったく表示したくない場合です。コンポーネントは何らかのものを返す必要があります。この場合、nullを返すことができます。

if (isPacked) {
return null;
}
return <li className="item">{name}</li>;

isPackedがtrueの場合、コンポーネントは何も、つまりnullを返します。そうでない場合は、レンダリングするJSXを返します。

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

実際には、コンポーネントからnullを返すことは一般的ではありません。レンダリングしようとする開発者を驚かせる可能性があるためです。多くの場合、親コンポーネントのJSXでコンポーネントを条件付きで含めたり除外したりします。その方法は次のとおりです!

JSXの条件付きインクルード

前の例では、コンポーネントから返されるJSXツリー(存在する場合)を制御しました。レンダリング出力に重複があることに既に気づいているかもしれません。

<li className="item">{name}</li>

は、

<li className="item">{name}</li>

どちらの条件分岐も<li className="item">...</li>を返します。

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

この重複は有害ではありませんが、コードの保守性を難しくする可能性があります。classNameを変更したい場合はどうでしょうか?コードの2箇所で変更する必要があります!このような状況では、JSXを条件付きで含めることで、コードをよりDRY (Don't Repeat Yourself)にすることができます。

条件演算子(三項演算子)(? :)

JavaScriptには、条件式を記述するための簡潔な構文があります — 条件演算子 または「三項演算子」です。

これの代わりに

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

これを記述できます。

return (
<li className="item">
{isPacked ? name + ' ✅' : name}
</li>
);

isPackedがtrueの場合(?)、name + ' ✅'をレンダリングし、そうでない場合(:)、nameをレンダリングする、という意味です。

詳細

これらの2つの例は完全に同等ですか?

オブジェクト指向プログラミングのバックグラウンドをお持ちの方であれば、上記の2つの例は微妙に異なるだろうと考えるかもしれません。その理由は、いずれかの例が<li>の2つの異なる「インスタンス」を作成する可能性があるためです。しかし、JSX要素は内部状態を持たず、実際のDOMノードではないため、「インスタンス」ではありません。それらは、設計図のような軽量な記述です。したがって、これらの2つの例は、実際には完全に同等です。状態の保持とリセットでは、この仕組みについて詳しく説明しています。

次に、完了したアイテムのテキストを<del>などの別のHTMLタグで囲みたいとします。より多くのJSXを各ケースにネストしやすくするために、さらに改行と括弧を追加できます。

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {isPacked ? (
        <del>
          {name + ' ✅'}
        </del>
      ) : (
        name
      )}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

このスタイルは単純な条件には適していますが、適度に使用してください。コンポーネントがネストされた条件付きマークアップが多すぎて混乱する場合は、子コンポーネントを抽出することで整理することを検討してください。Reactでは、マークアップはコードの一部であるため、変数や関数などのツールを使用して複雑な式を整理できます。

論理AND演算子 (&&)

もう一つのよく使われるショートカットは、JavaScriptの論理AND演算子(&&)です。Reactコンポーネント内では、条件がtrueの場合にいくつかのJSXをレンダリングする場合、またはそうでない場合は何もレンダリングしない場合によく使用されます。&&を使用すると、isPackedtrueの場合にのみチェックマークを条件付きでレンダリングできます。

return (
<li className="item">
{name} {isPacked && '✅'}
</li>
);

isPackedがtrueの場合(&&)、チェックマークをレンダリングし、そうでない場合は何もレンダリングしない、という意味です。

これが動作している様子です。

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✅'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

JavaScriptの&&式は、左辺(条件)がtrueの場合、右辺の値(この場合はチェックマーク)を返します。しかし、条件がfalseの場合、式全体がfalseになります。ReactはfalseをJSXツリーの「穴」と見なし、nullundefinedと同様に、その場所に何もレンダリングしません。

落とし穴

&&の左辺に数値を置かないでください。

条件をテストするために、JavaScriptは左辺を自動的にブール値に変換します。ただし、左辺が0の場合、式全体はその値(0)になり、Reactは何もレンダリングするのではなく0を喜んでレンダリングします。

たとえば、よくある間違いはmessageCount && <p>New messages</p>のようなコードを書くことです。messageCount0の場合、何もレンダリングしないと考えるのは簡単ですが、実際には0自体がレンダリングされます!

修正するには、左辺をブール値にします。messageCount > 0 && <p>New messages</p>

JSXを条件付きで変数に代入する

ショートカットが通常のコード記述の邪魔になる場合は、if文と変数を使用してみましょう。letで定義された変数は再代入できるので、まず表示したいデフォルトの内容、つまり変数名を指定します。

let itemContent = name;

isPackedtrueの場合、if文を使用してJSX式をitemContentに再代入します。

if (isPacked) {
itemContent = name + " ✅";
}

中括弧は「JavaScriptへの窓」を開きます。 計算済みの式をJSX内にネストし、中括弧で変数を返されるJSXツリーに埋め込みます。

<li className="item">
{itemContent}
</li>

このスタイルは最も冗長ですが、最も柔軟性も高いです。動作例を示します。

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = name + " ✅";
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

前述と同様に、これはテキストだけでなく、任意のJSXにも有効です。

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = (
      <del>
        {name + " ✅"}
      </del>
    );
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

JavaScriptに慣れていない場合、これらのスタイルのバリエーションは最初は圧倒的に感じるかもしれません。しかし、これらを学ぶことで、あらゆるJavaScriptコード(Reactコンポーネントだけではありません!)の読み書きに役立ちます。まずは好きなスタイルを選び、他のスタイルの書き方を忘れた場合は、このリファレンスを再度参照してください。

まとめ

  • Reactでは、JavaScriptを使用して分岐ロジックを制御します。
  • if文を使用して、JSX式を条件付きで返すことができます。
  • 中括弧を使用して、JSXを条件付きで変数に保存し、他のJSXの中に含めることができます。
  • JSXにおいて、{cond ? <A /> : <B />}は「condが真の場合、<A />をレンダリングし、そうでない場合は<B />をレンダリングする」という意味です。
  • JSXにおいて、{cond && <A />}は「condが真の場合、<A />をレンダリングし、そうでない場合は何もレンダリングしない」という意味です。
  • ショートカットは一般的ですが、通常のifを好む場合は使用しなくても構いません。

課題 1 3:
? :を使用して、未完了のアイテムにアイコンを表示します

isPackedtrueでない場合、条件演算子(cond ? a : b)を使用して❌をレンダリングします。

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✅'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}