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

Ваши компоненти часто потребують відображати різний контент в залежності від умови. У 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>
  );
}

Спробуйте змінити код цього прикладу та подивіться, як змінюється результат!

Зверніть увагу, як ви створюєте розгалужену логіку використовуючи if та return із JavaScript. У 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, коли умова є істинною, або нічого не рендерити в іншому випадку. З && ви могли б умовно відрендерити позначку, лише якщо 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>
  );
}