Один из самых популярных вопросов, возникающих при изучении React ㅡ как отображать или не отображать компоненты в зависимости от некоторого условия. Дело осложняется тем, что привычная конструкция if...else внутри JSX тегов не работает. В этой статье мы рассмотрим несколько способов написания условных конструкций, которые будут полезны любому начинающему разработчику на React.
JSX
JSX (JavaScript XML) ㅡ XML-подобный синтаксис, используемый в React для более простого и привычного задания разметки. Из документации по React:
JSX is a preprocessor step that adds XML syntax to JavaScript. You can definitely use React without JSX but JSX makes React a lot more elegant.
JSX не более чем синтаксический сахар, позволяющий описывать компоненты подобно вёрстке на HTML. Использование JSX при разработке на React не обязательно, но очень удобно и делает код нагляднее. JSX не поддерживается современными браузерами, и при компиляции JSX-выражения заменяются на вызов функции:
React.createElement(component, props, ...children)
Простой пример показывает, как выглядит код с использованием технологии JSX и без неё. Протестировать преобразование JSX в JS можно с помощью онлайн-сервиса Babel REPL.
// with JSX
class Hello extends React.Component {
render() {
return <div className='welcome'><h1>Hello, {this.props.name}!</h1></div>
}
}
ReactDOM.render(
<Hello name='Alex' />,
document.getElementById('root')
);
// without JSX
class Hello extends React.Component {
render() {
return
React.createElement('div', {className: 'welcome'},
React.createElement('h1', null, `Hello, ${this.props.name}`)
);
}
}
ReactDOM.render(
React.createElement(Hello, {name: 'Alex'}, null),
document.getElementById('root')
);
JSX допускает внутри себя выражения JavaScript (JavaScript Expressions), заключённые в фигурные скобки. Но в силу того, что теги JSX заменяются на вызов функции, внутри них нельзя использовать конструкции, не являющиеся выражениями JavaScript (if..else, for, switch и др) и приводящие к невалидному JavaScript коду на выходе.
Следующий код вызовет ошибку при компиляции.
class Navigation extends React.Component {
render (){
return (
<div id='nav'>
<Link to='/home'>Home</Link>
<Link to='/home'>Services</Link>
<Link to='/home'>About</Link>
{
if (this.props.loggedIn) <SignOutButton/>
else <SignInButton/>
}
</div>
)
}
}
Но есть несколько способов условного рендеринга элементов, позволяющих обойти это ограничение.
Способ 1. Тернарный оператор
В случае, если вариантов ровно два, хорошо подойдёт тернарный оператор. Главное, не забывать, что каждая ветка должна возвращать только один тег, и дополнительно оборачивать возвращаемый результат в <div>
, если это не так.
class Login extends React.Component {
render() {
return (
<div id='login'>
{(!this.props.loggedIn)
? <SignInButton/>
: (
<div>
<span>{this.props.userName}</span>
<SignOutButton/>
</div>
)
}
</div>
)
}
}
Способ 2. Операторы && и ||
Стандартные логические операторы работают одинаково во многих языках программирования. Они последовательно вычисляют перечисленные выражения, пока не встретят истинное (в случае &&) или ложное (в случае ||); последующие выражения в таком случае не вычисляются. Ложью считаются значения:
- false
- 0 (нуль)
- "" (пустая строка)
- null
- undefined
- NaN
Значения false, null, undefined, и true игнорируются при выводе их внутри JSX тегов, благодаря чему мы не получим в результате ничего "лишнего". А вот с нулём стоит быть осторожным ㅡ он будет напечатан. Если же вы хотите отобразить игнорируемые значения, их нужно явно преобразовать к строке.
class Welcome extends React.Component {
render() {
return (
<div id='welcome'>
Hello, {(this.props.userName || 'Anonymous')}!
{(this.props.loggedIn && 'I know you.')}
{(this.props.messages.length > 0 &&
'You have ' + this.props.messages.length +' incoming messages')}
This is undefined: {String(undefinedVar)}
</div>
)
}
}
Способ 3. Немедленно вызываемая функция
Можно объявить функцию и сразу же её вызвать.
class Welcome extends React.Component {
render() {
return (
<div id='welcome'>
Hello, you are
{(() => {
switch (this.props.role) {
case "admin": return "administrator";
case "moderator": return "moderator";
case "user": return "just user";
default: return "guest";
}
}
)()}
of this site.
</div>
)
}
}
Способ 4. В теле функции render
Достаточно вынести логику формирования страницы из выражения return выше в тело функции render, и никаких ограничений не будет.
class Login extends React.Component {
render() {
let button;
if (this.props.loggedIn) button = <SignOutButton/>
else button = <SignInButton/>
return (
<div id='login'>
{button}
</div>
)
}
}
Препятствовать отображению компонента также можно следующим способом.
class ErrorMessage extends React.Component {
if (!this.props.error) {
return null;
}
render() {
<div>Error: {this.props.errorMessage}</div>
}
}
Способ 5. В отдельной функции
Для большей аккуратности логику можно вынести в отдельную функцию.
class Login extends React.Component {
renderLoginButton() {
if (this.props.loggedIn) return <SignOutButton/>
else return <SignInButton/>
}
render() {
return (
<div id='login'>
{this.renderLoginButton()}
</div>
)
}
}
Ссылки
- React Without JSX
- If-Else in JSX
- How to Add If…Else Statements in JSX
- Conditional Rendering