Към съдържанието
wannacode

Условия: празно, грешка, зарежда

Накратко: Условното рендериране показва само частта от разметката, която отговаря на данните (зарежда, грешка, празно или списък); в React с && и тернарен оператор.

Проблем

Зарежда, грешка и списък едновременно

Каталогът е зареден. Под заглавието обаче пише „Зареждане…“, после „Продуктите не се заредиха.“, после „Няма продукти в тази категория.“ и чак тогава са продуктите.

React

Компонентът Catalog получава status и продуктите като props. Той обаче не ги гледа: рисува всичките четири части винаги. Купувачът не знае на кое да вярва, а екранният четец прочита и грешката, и продуктите.

Идеята

Частта, която отговаря на данните

Каталогът има четири състояния: зарежда, грешка, празно и готов. На екрана трябва да е само частта за текущото. Това е условно показване: компонентът решава какво да върне според данните.

В JSX има три начина, според това колко неща се сменят:

// Цял друг изглед: if и ранен return
if (status === 'loading') return <p role="status">Зареждане…</p>;
// Едно от две: ? :
<p>{product.stock === 0 ? 'Изчерпан' : 'В наличност'}</p>
// Нещо или нищо: &&
{status === 'error' && <p role="alert">Продуктите не се заредиха.</p>}

if е обикновеният JavaScript: компонентът е функция и може да върне различен JSX. ? : и && са изрази, затова могат да стоят вътре в JSX, в {}.

Двете библиотеки решават едно и също по свой начин: React с JavaScript около разметката, Vue с атрибути в нея. Резултатът е един и същ: елементът на невярното условие го няма в DOM-а. Не е скрит, а не съществува.

Тук status е даден като prop. Как каталогът го сменя сам, докато зарежда от API, е в модула „Ефекти, данни и формуляри“.

Виж го

Виж кой клон влиза

Една стойност влиза в компонента, а в DOM-а влиза само един клон. Гледай колона „DOM“ при всяка смяна на status.

function Catalog({ status, products }) {
if (status === 'loading') return <p>Зареждане…</p>;
if (status === 'error') return <p>Грешка</p>;
if (status === 'empty') return <p>Няма продукти</p>;
return <ul>…</ul>;
}
// в DOM, но скрито
<p hidden={status !== 'loading'}>Зареждане…</p>
Стъпка по стъпка, с думи
  1. status идва в компонента като prop. Тя е 'loading': първото if съвпада и компонентът връща само <p>Зареждане…</p>. В DOM-а влиза само той.
  2. status става 'error'. Библиотеката рисува наново: сега съвпада вторият клон. <p>Зареждане…</p> излиза от DOM-а, а съобщението за грешка влиза.
  3. 'empty': само съобщението, че категорията е празна.
  4. 'ready': нито едно if не съвпада и компонентът стига до последния return: списъка. В DOM-а е винаги един клон.
  5. Другият начин: елементът се рисува винаги, а атрибутът hidden решава дали се вижда. При loading hidden е false и текстът се вижда.
  6. status става 'ready'. Елементът остава в DOM-а, с всичко в него, и само получава display: none. Не се вижда, а екранният четец го прескача.
  7. Условие с if, ? : или && маха елемента от DOM-а. hidden само го скрива. Махането е по подразбиране, а скриването е за нещо, което се сменя често или трябва да пази написаното.

Първата част е условно показване: при нова стойност старият елемент излиза от DOM-а, а новият влиза. Втората част е скриване: елементът е в DOM-а през цялото време и само се показва или скрива. За нея е краят на урока.

Пробвай

Четирите състояния

Catalog рисува всичко наведнъж. Направи го така, че във всеки раздел да е само частта на неговото състояние: „Зареждане…“ при loading, грешката при error, „Няма продукти в тази категория.“ при empty и списъкът при ready. Другите части да ги няма в страницата.

React

Пробвай

Попълни

„Зареждане…“ трябва да се вижда само докато status е loading.

{status === 'loading'  <p role="status">Зареждане…</p>}

Идеята

Изчерпан: значка и изключен бутон

Условието не е само за цели части. В картата на продукта то сменя текст и атрибут:

{product.stock === 0 ? (
<p className="badge">Изчерпан</p>
) : (
<p>Остават {product.stock} бр.</p>
)}
<button type="button" disabled={product.stock === 0}>Добави</button>

disabled={true} слага атрибута disabled, а disabled={false} го маха.

Изключеният бутон не се натиска, не взима фокус с Tab, а екранният четец казва, че е недостъпен. Значката казва защо. Бутон, който се натиска, а после казва „няма наличност“, е по-лош: купувачът разбира едва след клика.

Пробвай

Изчерпан

Направи ProductCard така, че при stock 0 да показва „Изчерпан“ и бутонът „Добави“ да е изключен. Иначе да показва „Остават 5 бр.“ и бутонът да работи. Сиренето е изчерпано.

React

Идеята

Нулата на екрана

&& не връща true или false. Той връща лявата страна, ако тя е невярна, и дясната, ако е вярна. React рисува числата, включително 0, и не рисува false, null и undefined. Затова:

{items.length && <p>В кошницата: {items.length}</p>} // празна кошница: 0
{items.length > 0 && <p>В кошницата: {items.length}</p>} // празна кошница: нищо

Пред && слагай сравнение, което дава true или false: > 0, === 0, !== null. Друг път е ? : с null за „нищо“.

Пробвай

Намери грешката

При празна кошница в горния ъгъл пише „0“.

Избери реда с грешката.

Идеята

Махнато или скрито

Понякога елементът трябва да остане в DOM-а и само да не се вижда. Тук и двата панела имат поле за бележка. Отвори двата, напиши нещо, затвори ги и ги отвори пак.

React
В първия раздел панелът е с {removedOpen && …}: при затваряне го няма, а при отваряне е нов, с празно поле. Във втория е с атрибута hidden={!hiddenOpen}: той остава в DOM-а със display: none и пази написаното.

Махането е по подразбиране: по-малко елементи в страницата, нищо скрито за екранния четец. Скриването е за части, които се отварят и затварят често или трябва да пазят какво е въведено в тях.

По-точно, за любопитните

hidden и CSS. hidden работи през display: none от стиловете на браузъра. Ако CSS правило даде на елемента display: flex, правилото печели и елементът пак се вижда. Тогава скриваш с клас или със style={{ display: 'none' }}.

Едно и също място, един и същ елемент. При {isSearch ? <input placeholder="Търси" /> : <input placeholder="Код" />} и двата клона са <input> на едно и също място. React запазва елемента и сменя само placeholder, а написаното остава. Ако трябват два различни елемента, дай им различни key.

return null. Компонент, който не трябва да рисува нищо, връща null.

Класовите компоненти в стария код правят същото в метода render().

aria-hidden не е скриване. aria-hidden="true" скрива елемента само от екранния четец, а на екрана той остава. За скриване от всички е display: none: hidden, v-show или махане.

Упражнение

Кошницата

App показва три кошници: празна, с продукти и с изчерпан продукт. Направи Cart така, че празната да казва „Кошницата е празна.“, без списък и сума, и бутонът „Поръчай“ да е изключен. Пълната показва редовете, „Общо: 4,00 €“ и работещ бутон. Изчерпаният продукт има значка „Изчерпан“, над бутона пише „Махни изчерпаните продукти, за да поръчаш.“ (role alert), а бутонът е изключен.

React

Тест

Провери се

1. Условието за „Зареждане…“ е невярно. Какво има на негово място в DOM-а?
2. Кошницата е празна. Какво рисува {items.length && <p>В кошницата: {items.length}</p>}?
3. Кое е най-подходящо, за да покаже компонентът само „Зареждане…“, докато status е loading?
4. Панел с поле за бележка се отваря и затваря често, а написаното трябва да остане. Кое е по-подходящо?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·