Умовний рендеринг

Ваші компоненти часто матимуть потребу відображати різні речі в залежності від різних умов. У React ви можете умовно рендерити JSX, використовуючи синтаксис JavaScript: оператори if, && та ? :.

You will learn

  • Як повертати різний JSX в залежності від умови
  • Як умовно включати або виключати частину JSX
  • Загальні скорочення умовного синтаксису, з якими ви зустрінетеся в кодових базах React

Умовне повернення JSX

Припустимо, у вас є компонент PackingList, який рендерить кілька компонентів Item, які можуть бути запаковані або ні:

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

export default function PackingList() {
  return (
    <section>
      <h1>Список речей для пакування Саллі Райд(Sally Ride)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </ul>
    </section>
  );
}

Зверніть увагу, що деякі компоненти Item мають проп isPacked встановлений на true замість false. Ви хочете додати позначку (✔) до запакованих речей, якщо 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)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </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)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </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? Вам доведеться зробити це в двох місцях вашого коду! У такій ситуації ви могли б умовно включити трохи JSX, щоб зробити ваш код більш DRY.

Умовний (тернарний) оператор (? :)

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.

Deep Dive

Чи ці два приклади повністю еквівалентні?

Якщо у вас є досвід з об’єктно-орієнтованого програмування, ви можете припустити, що два приклади вище є трохи різними, оскільки один з них може створити дві різні “сутності” <li>. Але JSX-елементи не є “сутностями”, оскільки вони не містять жодного внутрішнього стану і не є реальними вузлами DOM. Це легкі описи, подібні до креслень. Таким чином, ці два приклади, насправді, є абсолютно еквівалентними. Збереження та скидання стану детально розповідає про те, як це працює.

Тепер, скажімо, ви хочете обгорнути текст елемента в інший HTML-тег, наприклад <del>, щоб його перекреслити. Ви можете додати ще більше нових рядків і дужок, щоб було легше використовувати вкладенний JSX в кожному з випадків:

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

export default function PackingList() {
  return (
    <section>
      <h1>Список речей для пакування Саллі Райд(Sally Ride)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </ul>
    </section>
  );
}

Цей стиль добре працює для простих умов, але використовуйте його з розумом. Якщо ваші компоненти стають заплутаними через велику вкладену умовну розмітку, подумайте над “розпакуванням” дочірніх компонентів, щоб все виглядало охайніше. У React розмітка є частиною вашого коду, тому ви можете використовувати такі інструменти, як змінні та функції, щоб спростити складні вирази.

Логічний оператор AND (&&)

Ще одне поширене скорочення, з яким ви зіткнетеся — логічний оператор AND (&&) JavaScript. Всередині компонентів React він часто з’являється, коли ви хочете відрендерити деякий JSX, коли умова є true, або нічого не рендерити в іншому випадку. З && ви могли б умовно відрендерити позначку, лише якщо isPacked є true:

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

Ви можете читати це як “якщо isPacked, тоді (&&) відрендерити позначку, в іншому випадку нічого не рендерити”.

Ось він в дії:

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

export default function PackingList() {
  return (
    <section>
      <h1>Список речей для пакування Саллі Райд(Sally Ride)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </ul>
    </section>
  );
}

JavaScript && вираз повертає значення своєї правої сторони (у нашому випадку, позначку) якщо ліва сторона (наша умова) true. Але якщо умова false, то весь вираз стає false. React розглядає false як “діру” в JSX дереві, так само як null або undefined, і не рендерить нічого на його місці.

Pitfall

Не ставте числа зліва від &&.

Щоб перевірити умову, JavaScript автоматично перетворює ліву сторону на булеве значення. Однак, якщо ліва сторона є 0, то весь вираз отримує це значення (0), і React з задоволенням відрендерить 0 замість нічого.

Наприклад, поширеною помилкою є написання коду типу messageCount && <p>New messages</p>. Легко припустити, що він нічого не рендерить, коли messageCount дорівнює 0, але насправді він рендерить сам 0!

Щоб виправити це, зробіть ліву сторону булевою: messageCount > 0 && <p>New messages</p>.

Умовне присвоєння JSX змінній

Коли скорочення заважають писати звичайний код, спробуйте використати оператор if та змінну. Ви можете переприсвоювати змінні, визначені за допомогою let, тому почніть з задання вмісту за замовчуванням, який ви хочете відобразити, name:

let itemContent = name;

Використовуйте оператор if для переприсвоєння JSX-виразу itemContent, якщо isPacked дорівнює true:

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)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </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)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </ul>
    </section>
  );
}

Якщо ви не знайомі з JavaScript, то ця різноманітність стилів може спершу здатися вам занадто складною. Однак, їх знання допоможе вам читати та писати будь-який код JavaScript — і не тільки компоненти React! Виберіть той, який вам більше подобається для початку, а потім зверніться до цього довідника знову, якщо ви забудете, як працюють інші.

Recap

  • У React ви керуєте розгалуженою логікою за допомогою JavaScript.
  • Ви можете повернути JSX-вираз умовно за допомогою оператора if.
  • Ви можете умовно зберегти деякий JSX у змінну, а потім включити його в інший JSX, використовуючи фігурні дужки.
  • У JSX, {cond ? <A /> : <B />} означає “якщо cond, відобразити <A />, інакше <B />.
  • У JSX, {cond && <A />} означає “якщо cond, відобразити <A />, інакше нічого”.
  • Ці скорочення є поширеними, але ви не повинні їх використовувати, якщо вам більше подобається простий if.

Challenge 1 of 3:
Показати значок для незавершених елементів за допомогою ? :

Використовуйте умовний оператор (cond ? a : b) для відображення ❌, якщо isPacked не дорівнює true.

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

export default function PackingList() {
  return (
    <section>
      <h1>Список речей для пакування Саллі Райд(Sally Ride)</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Космічний костюм" 
        />
        <Item 
          isPacked={true} 
          name="Шолом із золотим листям" 
        />
        <Item 
          isPacked={false} 
          name="Фото Тем О'Шонессі(Tam O'Shaughnessy)" 
        />
      </ul>
    </section>
  );
}