Условный рендеринг
Вашим компонентам нужно часто отображать различные вещи в зависимости от различных условий. В 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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </ul> </section> ); }
Этот стиль хорошо работает для простых условий, но используйте его в меру. Если ваши компоненты становятся запутанными из-за слишком большого количества вложенной условной разметки, подумайте об выделении дочерних компонентов, чтобы навести порядок. В React разметка является частью кода, поэтому вы можете использовать такие инструменты, как переменные и функции, чтобы привести в порядок сложные выражения.
Логический оператор И(&&)
Еще одно часто встречающееся сокращение 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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </ul> </section> ); }
JavaScript выражение && возвращает значение правой части (в нашем случае это галочка), если левая часть (наше условие) является true. Но если наше условие — false, тогда всё выражение становится false. React рассматривает false как “пустое место” в дереве JSX, прямо как null или undefined, и ничего не рендерит на этом месте.
Условное присвоение 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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </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>Список вещей Салли Райд</h1> <ul> <Item isPacked={true} name="Космический скафандр" /> <Item isPacked={true} name="Шлем с золотым листом" /> <Item isPacked={false} name="Фотография Тэма" /> </ul> </section> ); }