- Quick Start
- Installation
- Describing the UI
- Adding Interactivity
- Managing State
- Escape Hatches
GET STARTED
LEARN REACT
Умовний рендеринг
Вашим компонентам часто буде потрібно відображати різні речі залежно від різних умов. У React ви можете умовно рендерити JSX, використовуючи синтаксис JavaScript, наприклад, оператори if
, &&
та ? :
.
- Як повернути різні 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'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
зі значенням 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'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
? Вам доведеться зробити це у двох місцях коду! У такій ситуації ви можете умовно включити трохи JSX, щоб зробити ваш код більш СУХИМ.
Умовний (тернарний) оператор (? :
)
JavaScript має компактний синтаксис для запису умовного виразу - умовний оператор або "тернарний оператор".
Замість цього:
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
Можна записати так:
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
Це можна прочитати як "якщо isPacked
істинно, то (?
) відрендерити name + ' ✔'
, інакше (:
) відрендерити name
".
Чи є ці два приклади повністю еквівалентними?
Якщо ви прийшли з об'єктно-орієнтованого програмування, ви можете припустити, що два наведені вище приклади ледь помітно відрізняються, оскільки один з них може створити два різних "екземпляри" <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'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. Всередині React-компонентів він часто виникає, коли ви хочете відрендерити якийсь JSX, коли умова істинна, або не відрендерити нічого в іншому випадку. За допомогою &&
ви можете умовно відрендерити галочку, тільки якщо isPacked
є істинною
:
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'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 вираз && повертає значення своєї правої частини (у нашому випадку, галочки), якщо ліва частина (наша умова) істинна
. Але якщо умова має значення false
, то весь вираз стає false
. React вважає false
"діркою" у дереві JSX, так само як null
або undefined
, і не відображає нічого на його місці.
не ставити числа ліворуч від &&
.
Для перевірки умови JavaScript автоматично перетворює ліву частину в булеву. Однак, якщо ліва частина має значення 0
, то весь вираз отримує це значення (0
), і React з радістю відобразить 0
, а не нічого.
Наприклад, поширеною помилкою є написання коду типу messageCount && <p>New messages</p>
. Легко припустити, що він нічого не відрендерить, коли messageCount
є 0
, але насправді він відрендерить сам 0
!
Щоб виправити це, зробіть ліву частину логічною: messageCount > 0 && <p>New messages</p>
.
Умовне присвоєння JSX змінній
Коли комбінації клавіш заважають написанню простого коду, спробуйте використати інструкцію if
та змінну. Ви можете перепризначати змінні, визначені за допомогою let
, тому почніть з надання вмісту за замовчуванням, який ви хочете відображати, з назвою:
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'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.
- Ви можете повернути вираз JSX умовно за допомогою інструкції
if
. - Ви можете умовно зберегти деякий JSX у змінну, а потім включити його до іншого JSX за допомогою фігурних дужок.
- У JSX
{cond ? <A /> : <B />}
означає "якщоcond
, рендерити<A />
, інакше<B />
". - У JSX
{cond && <A />}
означає "якщоcond
, рендерити<A />
, інакше нічого". - Ці комбінації є стандартними, але ви можете не використовувати їх, якщо віддаєте перевагу звичайним
if
.
Показувати іконку для незавершених елементів з ? :
Використовуйте умовний оператор (cond ? a : b
) для виведення ❌, якщо isPacked
не є істинним
.
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>
);
}
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>
);
}
Показати важливість елемента за допомогою &&
У цьому прикладі кожен Item
отримує числову важливість пропу
. Використовуйте оператор &&
для виділення "(Важливість: X)" курсивом, але лише для елементів, які мають ненульову важливість. Ваш список елементів має виглядати так:
- Скафандр (Важливість: 9)
- Шолом із золотим листям
- Фотографія Там(Важливість: 6)
Не забудьте додати пробіл між двома мітками!
function Item({ name, importance }) {
return (
<li className="item">
{name}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
importance={9}
name="Space suit"
/>
<Item
importance={0}
name="Helmet with a golden leaf"
/>
<Item
importance={6}
name="Photo of Tam"
/>
</ul>
</section>
);
}
Це має спрацювати:
function Item({ name, importance }) {
return (
<li className="item">
{name}
{importance > 0 && ' '}
{importance > 0 &&
<i>(Importance: {importance})</i>
}
</li>
);
}
export default function PackingList() {
return (
<section>
<h1>Sally Ride's Packing List</h1>
<ul>
<Item
importance={9}
name="Space suit"
/>
<Item
importance={0}
name="Helmet with a golden leaf"
/>
<Item
importance={6}
name="Photo of Tam"
/>
</ul>
</section>
);
}
Зверніть увагу, що ви повинні написати importance > 0 && ...
, а не importance && ...
, щоб якщо значення
дорівнює 0
, 0
не виводилося як результат!
У цьому рішенні використовуються дві окремі умови для вставки пробілу між назвою та міткою важливості. Крім того, ви можете використати фрагмент з пробілом на початку: importance > 0 && <> <i>...</i></>
або додати пробіл безпосередньо всередині <i>
: importance > 0 && <i> ...</i>
.
Рефакторинг серії ? :
до if
та змінних
Цей Drink
компонент використовує низку ? :
умов для відображення різної інформації залежно від того, чи проп name
є "tea"
або "coffee"
. Проблема полягає в тому, що інформація про кожен напій розподілена між декількома умовами. Рефакторингуйте цей код, щоб використовувати один оператор if
замість трьох умов ? :
.
function Drink({ name }) {
return (
<section>
<h1>{name}</h1>
<dl>
<dt>Part of plant</dt>
<dd>{name === 'tea' ? 'leaf' : 'bean'}</dd>
<dt>Caffeine content</dt>
<dd>{name === 'tea' ? '15–70 mg/cup' : '80–185 mg/cup'}</dd>
<dt>Age</dt>
<dd>{name === 'tea' ? '4,000+ years' : '1,000+ years'}</dd>
</dl>
</section>
);
}
export default function DrinkList() {
return (
<div>
<Drink name="tea" />
<Drink name="coffee" />
</div>
);
}
Після того, як ви переробили код для використання if
, чи є у вас подальші ідеї, як його спростити?
Є кілька способів, але ось один з них:
function Drink({ name }) {
let part, caffeine, age;
if (name === 'tea') {
part = 'leaf';
caffeine = '15–70 mg/cup';
age = '4,000+ years';
} else if (name === 'coffee') {
part = 'bean';
caffeine = '80–185 mg/cup';
age = '1,000+ years';
}
return (
<section>
<h1>{name}</h1>
<dl>
<dt>Part of plant</dt>
<dd>{part}</dd>
<dt>Caffeine content</dt>
<dd>{caffeine}</dd>
<dt>Age</dt>
<dd>{age}</dd>
</dl>
</section>
);
}
export default function DrinkList() {
return (
<div>
<Drink name="tea" />
<Drink name="coffee" />
</div>
);
}
Тут інформація про кожен напій згрупована разом, а не розпорошена по декількох умовах. Це полегшує додавання нових напоїв у майбутньому.
Іншим рішенням може бути повне видалення умови шляхом переміщення інформації в об'єкти:
const drinks = {
tea: {
part: 'leaf',
caffeine: '15–70 mg/cup',
age: '4,000+ years'
},
coffee: {
part: 'bean',
caffeine: '80–185 mg/cup',
age: '1,000+ years'
}
};
function Drink({ name }) {
const info = drinks[name];
return (
<section>
<h1>{name}</h1>
<dl>
<dt>Part of plant</dt>
<dd>{info.part}</dd>
<dt>Caffeine content</dt>
<dd>{info.caffeine}</dd>
<dt>Age</dt>
<dd>{info.age}</dd>
</dl>
</section>
);
}
export default function DrinkList() {
return (
<div>
<Drink name="tea" />
<Drink name="coffee" />
</div>
);
}