null

Альтернатива if...else в JSX

Один из самых популярных вопросов, возникающих при изучении 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>
    )
  }
}

 

Ссылки

  1. React Without JSX
  2. If-Else in JSX
  3. How to Add If…Else Statements in JSX
  4. Conditional Rendering