2.8 Отрисовка по условию


В React вы можете создавать определенные компоненты, которые инкапсулируют необходимое поведение. Далее, в зависимости от состояния вашего приложения, можно отрисовывать только некоторые из них.


В React отрисовка по условию работает также, как и условия в JavaScript. Используйте условные операторы JavaScript, такие как if или тернарный оператор, чтобы создавать элементы, представляющие текущее состояние. Затем позвольте React обновить UI, чтобы привести элементы и состояние в соответствие.

Рассмотрим два компонента:


Код
        
  function ErrorMessage(props) {
    return <h3>Произошла ошибка на сервере. Не удалось сохранить ваши данные.</h3>;
  }

  function SuccessMessage(props) {
    return <h3>Ваши данные успешно сохранены!</h3>;
  }
    

Давайте создадим компонент Response, который отображает один из этих компонентов, в зависимости от результата сохранения данных на сервере:


Код
        
  function Response(props) {
    const isSuccess = props.isSuccess;
    if (isSuccess) {
      return <SuccessMessage/>;
    }
    return <ErrorMessage/>;
    //или
    //return isSuccess ? <SuccessMessage/> : <ErrorMessage/>
  }
  // Можете поменять на isSuccess={false}:
  ReactDOM.render(<Response isSuccess={true} />,  document.getElementById('root'));
    

Посмотреть в CodePen


Этот пример отрисовывает различные сообщения в зависимости от значения свойства isSuccess.



2.8.1 Хранение элементов в переменных


Вы можете использовать переменные, чтобы хранить элементы. Это позволяет отрисовывать по условию только часть компонента, в то время как оставшаяся часть не изменится.

Пусть у нас есть камин с двумя кнопками управления. Кнопки представим в виде компонентов SetFire и SnuffOut:


Код
        
  function SetFireButton(props) {
    return (<button className="orange" onClick={props.onClick}>Зажечь</button>);
  }

  function SnuffOutButton(props) {
    return (<button className="blue" onClick={props.onClick}>Потушить</button>);
  }
    

Далее создадим компонент с состоянием под названием FirePlace.

Он будет отрисовывать либо <SetFireButton />, либо <SnuffOutButton /> в зависимости от его текущего состояния. Также он будет отрисовывать компонент <Indicator />, показывающий сообщения о состоянии нашего камина:


Код
        
  class FirePlace extends React.Component {
    constructor(props) {
      super(props);
      this.onSetFire = this.onSetFire.bind(this);
      this.onSnuffOut = this.onSnuffOut.bind(this);
      this.state = {isBurning: false};
    }

    onSetFire() {
      this.setState({isBurning: true});
    }

    onSnuffOut() {
      this.setState({isBurning: false});
    }

    render() {
      const isBurning = this.state.isBurning;

      let button = null;
      if(isBurning){
        button = <SnuffOutButton onClick={this.onSnuffOut} />
      } else {
        button = <SetFireButton onClick={this.onSetFire} />;
      }
      return (
        <div>
          <Indicator isBurning={isBurning} />
          {button}
        </div>
      );
    }
  }
    

Посмотреть в CodePen


Использование переменной и оператора if – хороший способ отрисовывать компонент по условию. Но что если иногда хочется использовать более короткий синтаксис? Есть несколько способов встроить условия в JSX, рассмотрим их ниже.



2.8.2 Встроенный If с логическим оператором &&


Вы можете встраивать любые выражения в JSX, оборачивая их в фигурные скобки. Сюда относится и логический оператор && JavaScript. Это удобно для добавления элемента по условию:


Код
        
  function Console(props) {
    const errors = props.errors;
    return (
      <p>
        <h3>Attention!</h3>
        {errors.length > 0 &&
          <p>You have <b>{errors.length}</b> errors.</p>
        }
      </p>
    );
  }

  const errors = [
  'Failed to load resource: net::ERR_INSECURE_RESPONSE',
  'TypeError: e is undefined',
  'Uncaught ReferenceError: calculate is not defined'
  ];
  ReactDOM.render(<Console errors={errors} />, document.getElementById('root'));
    

Этот код работает, потому что в JavaScript true && выражение всегда возвращает выражение, а false && выражение всегда возвращает false.

Следовательно, если условие истинно, элемент появится в результате вывода прямо после &&. Если условие ложно, React пропустит его.



2.8.3 Встраивание If-else с помощью условного оператора


Другой метод отрисовать элемент по условию в JSX-выражении - использовать тернарный оператор JavaScript: условие ? true : false

В примере ниже мы используем его для отрисовки по условию маленького блока текста:


Код
        
  render() {
    const isSent = this.state.isSent;
    return (
      <p>
        <h3>Ваше сообщение {isSent ? 'успешно' : 'не'} отправлено</h3>
      </p>
    );
  }
    

Оператор может оказаться полезным и для более длинных выражений, даже если код может показаться менее читабельным:


Код
        
  render() {
    const isNew = this.state.isNew;
    return (
      <div>
        {isNew ? (
                <div>
                   <EditButton onClick={this.onEdit} />
                   <DeleteButton onClick={this.onDelete} />
                </div>
            ) : (
                <AddButton onClick={this.onAdd} />
            )
        }
      </div>
    );
  }
    

Как и в JavaScript, именно вам предстоит выбрать подходящий стиль кода, который будет максимально читабелен для всей вашей команды. Также помните, что как только условия становятся слишком сложными, возможно, наступило отличное время выделить компонент.



2.8.4 Предотвращение отрисовки компонента


В редких случаях вам нужно, чтобы компонент себя скрыл, даже если он был отрисован другим компонентом. Чтобы это сделать, нужно вернуть null, вместо результата его отрисовки.

В примере ниже <DangerAlert /> отрисовывается в зависимости от значения переменной состояния isDangerAlertShowed. Если её значение равно false, то компонент не отрисовывается:


Код
        
  function DangerAlert(props) {
    return (
      <h3 className="alert alert-danger">{props.text}</h3>
    );
  }

  class Application extends React.Component {
    constructor(props) {
      super(props);
      this.state = {isDangerAlertShowed: true}
      this.toggleDangerAlert = this.toggleDangerAlert.bind(this);
    }

    toggleDangerAlert() {
      this.setState(prevState => ({
        isDangerAlertShowed: !prevState.isDangerAlertShowed
      }));
    }

    render() {
      return (
        <div>
          {this.state.isDangerAlertShowed ?
              <DangerAlert text={'Внимание! В приложении возникли некоторые проблемы!'} /> : null}
          <button onClick={this.toggleDangerAlert}>
            {this.state.isDangerAlertShowed ? 'Скрыть' : 'Показать'}
          </button>
        </div>
      );
    }
  }

  ReactDOM.render(<Application />, document.getElementById('root'));
    

Посмотреть в CodePen


Когда мы возвращаем null из метода render, это никак не влияет на срабатывание методов жизненного цикла. К примеру, по-прежнему будут вызываться componentWillUpdate и componentDidUpdate.