Как иметь условные элементы и сохранять СУХОЙ с JSX Facebook React?


229

Как дополнительно включить элемент в JSX? Вот пример использования баннера, который должен быть в компоненте, если он был передан. Чего я хочу избежать, так это дублирования HTML-тегов в операторе if.

render: function () {
    var banner;
    if (this.state.banner) {
        banner = <div id="banner">{this.state.banner}</div>;
    } else {
        banner = ?????
    }
    return (
        <div id="page">
            {banner}
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

3
Что если у вас просто нет elseветки, это работает? Я не знаком с JSX ...
Том Фенек

1
Здорово, это помогло. Также см. Github.com/facebook/react/issues/690
Габриэль Флорит

Исчерпывающий список параметров для условного рендеринга в React: robinwieruch.de/conditional-rendering-react
Робин Вирух,

Ответы:


151

Просто оставьте баннер как неопределенный, и он не будет включен.


1
Я не могу думать ни о чем, кроме мусора рядом с этим ответом ... как будто я наткнулся на богов
Тиммерз

Работает nullтак же хорошо, как undefined? Также это та часть спецификации, что она работает таким образом и, следовательно, вряд ли сломается в будущем?
hippietrail

133

Как насчет этого. Давайте определим простой вспомогательный Ifкомпонент.

var If = React.createClass({
    render: function() {
        if (this.props.test) {
            return this.props.children;
        }
        else {
            return false;
        }
    }
});

И используйте это так:

render: function () {
    return (
        <div id="page">
            <If test={this.state.banner}>
                <div id="banner">{this.state.banner}</div>
            </If>
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

ОБНОВЛЕНИЕ: Поскольку мой ответ становится популярным, я чувствую себя обязанным предупредить вас о самой большой опасности, связанной с этим решением. Как указано в другом ответе, код внутри <If />компонента выполняется всегда независимо от того, является ли условие истинным или ложным. Поэтому в следующем примере произойдет сбой в случае, если bannerесть null(обратите внимание на доступ к свойству во второй строке):

<If test={this.state.banner}>
    <div id="banner">{this.state.banner.url}</div>
</If>

Вы должны быть осторожны, когда используете его. Я предлагаю прочитать другие ответы для альтернативных (более безопасных) подходов.

ОБНОВЛЕНИЕ 2: Оглядываясь назад, этот подход не только опасен, но и крайне обременителен. Это типичный пример того, когда разработчик (я) пытается перенести известные ему шаблоны и подходы из одной области в другую, но на самом деле это не работает (в данном случае другие языки шаблонов).

Если вам нужен условный элемент, сделайте это так:

render: function () {
    return (
        <div id="page">
            {this.state.banner &&
                <div id="banner">{this.state.banner}</div>}
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

Если вам также нужна ветвь else, просто используйте троичный оператор:

{this.state.banner ?
   <div id="banner">{this.state.banner}</div> :
   <div>There is no banner!</div>
}

Это намного короче, элегантнее и безопаснее. Я использую это все время. Единственным недостатком является то, что вы не можете делать else ifэто легко, но обычно это не так часто.

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

<h3>{this.state.banner.title || 'Default banner title'}</h3>

1
Спасибо. Я рекомендую прочитать этот github.com/facebook/react/issues/690. Эта ссылка была опубликована в комментариях под этим вопросом, и я заметил это после того, как опубликовал свое решение. Если вы проверите это, какой-то парень тоже упоминает это решение, но не рекомендует его. Лично я думаю, что все в порядке, по крайней мере, в случае с OP, но это правда, что этот путь не идеален (например, нет хорошего способа определить другую ветку). Во всяком случае, стоит прочитать.
тобик

1
Не уверен, что это все еще работает, так как последняя версия, кажется, требует, чтобы возвращаемое значение былоReactElement
srph

Просто оберните его другим элементом <span>или <div>.
тобик

Существует проблема, когда я пытался использовать это для рендеринга <td> или empty в <table>, потому что <noscript> неприемлем как дочерний элемент для <tr>. В противном случае это работает нормально для меня.
Грин Су

1
Если вы собираетесь это сделать, есть метод переноса, который довольно хорош: npmjs.com/package/jsx-control-statements
Стив

82

Лично я действительно считаю, что троичные выражения, показанные в ( JSX In Depth ), являются наиболее естественным способом, который соответствует стандартам ReactJs.

Смотрите следующий пример. Это немного грязно на первый взгляд, но работает довольно хорошо.

<div id="page">
  {this.state.banner ? (
    <div id="banner">
     <div class="another-div">
       {this.state.banner}
     </div>
    </div>
  ) : 
  null} 
  <div id="other-content">
    blah blah blah...
  </div>
</div>

Примечание: квадратные скобки совпадают с возвращаемым компонентом, поэтому ваш контент должен быть заключен в <div> </ div>.
JoeTidee

@Chiedo, почему вы предпочитаете популярный ответ? Кажется, это отлично работает.
Снеговик

2
@Moby Я думаю, что популярный ответ является более динамичным и приводит к более чистой функции рендеринга. При таком подходе, что произойдет, если у вас есть несколько условий? Теперь у вас будет безумие вложения с этим странным форматом, и если две отдельные области нуждаются в одинаковых изменениях, основанных на условных выражениях, вам теперь придется переписывать условные снова. Просто мое мнение, хотя! :)
Chiedo

Связанная страница также весьма полезна: facebook.github.io/react/docs/…
Neil

46

Вы также можете написать это как

{ this.state.banner && <div>{...}</div> }

Если state.bannerэто nullили undefined, правая часть условия будет пропущена.


41

Компонент Ifстиля опасен, потому что блок кода всегда выполняется независимо от условия. Например, это может вызвать исключение нулевой , если bannerэто null:

//dangerous
render: function () {
  return (
    <div id="page">
      <If test={this.state.banner}>
        <img src={this.state.banner.src} />
      </If>
      <div id="other-content">
         blah blah blah...
      </div>
    </div>
  );
}

Другой вариант - использовать встроенную функцию (особенно полезно с другими инструкциями):

render: function () {
  return (
    <div id="page">
      {function(){
        if (this.state.banner) {
          return <div id="banner">{this.state.banner}</div>
        }
      }.call(this)}
      <div id="other-content">
         blah blah blah...
      </div>
    </div>
  );
}

Еще один вариант из реагирующих вопросов :

render: function () {
  return (
    <div id="page">
      { this.state.banner &&
        <div id="banner">{this.state.banner}</div>
      }
      <div id="other-content">
         blah blah blah...
      </div>
    </div>
  );
}

2
Пока что из всех решений встроенная функция выглядит самой элегантной и простой. Что-нибудь, что нужно остерегаться?
suryasankar

22

&& + стиль кода + небольшие компоненты

Этот простой тестовый синтаксис + соглашение в стиле кода + небольшие ориентированные компоненты - для меня наиболее читаемый вариант. Вам просто нужно позаботиться о ложных значениях, таких как false, 0или "".

render: function() {
    var person= ...; 
    var counter= ...; 
    return (
       <div className="component">
          {person && (
            <Person person={person}/>
          )}
          {(typeof counter !== 'undefined') && (
            <Counter value={counter}/>
          )}
       </div>
    );
}

сделать запись

Синтаксис нотации ES7 stage-0 do do также очень хорош, и я буду использовать его, когда моя среда IDE правильно его поддерживает:

const Users = ({users}) => (
  <div>
    {users.map(user =>
      <User key={user.id} user={user}/>
    )}
  </div>
)  

const UserList = ({users}) => do {
  if (!users) <div>Loading</div>
  else if (!users.length) <div>Empty</div>
  else <Users users={users}/>
}

Подробнее здесь: ReactJs - Создание компонента «Если» ... хорошая идея?


Бесплатная неосновная информация: первый называетсяshort-circuit syntax
споспедра

1
@Deerloper Я думаю, что разница между && и & является одной из первых вещей, которую мы изучаем в компьютерных школах, но некоторые люди могут не знать, поэтому неплохо давать больше объяснений: en.wikipedia.org/wiki/Short-circuit_evaluation
Себастьян Лорбер,

Правда, но они учат этому как части классических условных высказываний. Я обнаружил, что многие люди не
Соспедра

22

Просто создайте функцию.

renderBanner: function() {
  if (!this.state.banner) return;
  return (
    <div id="banner">{this.state.banner}</div>
  );
},

render: function () {
  return (
    <div id="page">
      {this.renderBanner()}
      <div id="other-content">
        blah blah blah...
      </div>
    </div>
  );
}

Это шаблон, которому я лично следую все время. Делает код действительно чистым и легким для понимания. Более того, он позволяет вам выполнить рефакторинг Bannerв свой собственный компонент, если он становится слишком большим (или повторно используется в других местах).



11

Как уже упоминалось в ответах, JSX предоставляет вам два варианта

  • Тернарный оператор

    { this.state.price ? <div>{this.state.price}</div> : null }

  • Логическое соединение

    { this.state.price && <div>{this.state.price}</div> }


Тем не менее, они не работают для price == 0 .

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


Это не проблема JSX. Это само условие, даже в простой JS будет вести себя так же. Если вы хотите любое число, просто используйте typeof(this.state.price) === "number"в качестве условия (в любом варианте).
Kroltan

8

Этот компонент работает, когда у вас есть более одного элемента внутри ветви "if":

var Display = React.createClass({
  render: function () {
    if (!this.props.when) {
      return false;
    }
    return React.DOM.div(null, this.props.children);
  },
});

Использование:

render: function() {
  return (
    <div>
      <Display when={this.state.loading}>
        Loading something...
        <div>Elem1</div>
        <div>Elem2</div>
      </Display>
      <Display when={!this.state.loading}>
        Loaded
        <div>Elem3</div>
        <div>Elem4</div>
      </Display>
    </div>
  );
}

PS Кто-то считает, что эти компоненты не годятся для чтения кода. Но на мой взгляд, HTML с JavaScript хуже


Как и ответ «Если» здесь, он все равно будет выполнять ложное условие, даже если оно не отображается.
Киган 82

3

Большинство примеров с одной строкой «html», которая отображается условно. Это кажется читаемым для меня, когда у меня есть несколько строк, которые должны быть отображены условно.

render: function() {
  // This will be renered only if showContent prop is true
  var content = 
    <div>
      <p>something here</p>
      <p>more here</p>
      <p>and more here</p>
    </div>;

  return (
    <div>
      <h1>Some title</h1>

      {this.props.showContent ? content : null}
    </div>
  );
}

Первый пример хорош, потому что вместо того, nullчтобы условно отображать какой-то другой контент, такой как{this.props.showContent ? content : otherContent}

Но если вам просто нужно показать / скрыть контент, это еще лучше, так как булевы, нулевые и неопределенные игнорируются

render: function() {
  return (
    <div>
      <h1>Some title</h1>

      // This will be renered only if showContent prop is true
      {this.props.showContent &&
        <div>
          <p>something here</p>
          <p>more here</p>
          <p>and more here</p>
        </div>
      }
    </div>
  );
}


2

Я использую более явный ярлык: выражение немедленного вызова функции (IIFE):

{(() => {
  if (isEmpty(routine.queries)) {
    return <Grid devices={devices} routine={routine} configure={() => this.setState({configured: true})}/>
  } else if (this.state.configured) {
    return <DeviceList devices={devices} routine={routine} configure={() => this.setState({configured: false})}/>
  } else {
    return <Grid devices={devices} routine={routine} configure={() => this.setState({configured: true})}/>
  }
})()}

1

Я сделал https://www.npmjs.com/package/jsx-control-statements , чтобы сделать его немного легче, в основном это позволяет определить <If>условный как теги , а затем компилирует их в трехкомпонентный МФС , так что код внутри <If>только получает выполняется, если условие истинно.


1

Существует также действительно чистая однострочная версия ... {this.props.product.title || "Без названия" }

То есть:

render: function() {
            return (
                <div className="title">
                    { this.props.product.title || "No Title" }
                </div>
            );
        }

Это работает с моим образцом выше? Где не должен появляться баннер div, если нет баннера реквизита?
Джек Аллан

1

Я недавно сделал https://github.com/ajwhite/render-if, чтобы безопасно отображать элементы, только если предикат прошел .

{renderIf(1 + 1 === 2)(
  <span>Hello!</span>
)}

или

const ifUniverseIsWorking = renderIf(1 + 1 === 2);

//...

{ifUniverseIsWorking(
  <span>Hello!</span>
)}

1

Вы можете условно включить элементы, используя троичный оператор, например так:

render: function(){

         return <div id="page">

                  //conditional statement
                  {this.state.banner ? <div id="banner">{this.state.banner}</div> : null}

                  <div id="other-content">
                      blah blah blah...
                  </div>

               </div>
}

1

Вы можете использовать функцию и вернуть компонент и сохранить функцию визуализации

class App extends React.Component {
  constructor (props) {
    super(props);
    this._renderAppBar = this._renderAppBar.bind(this);
  }

  render () {
    return <div>
      {_renderAppBar()}

      <div>Content</div>

    </div>
  }

  _renderAppBar () {
    if (this.state.renderAppBar) {
      return <AppBar />
    }
  }
}

1

Вот мой подход с использованием ES6.

import React, { Component } from 'react';
// you should use ReactDOM.render instad of React.renderComponent
import ReactDOM from 'react-dom';

class ToggleBox extends Component {
  constructor(props) {
    super(props);
    this.state = {
      // toggle box is closed initially
      opened: false,
    };
    // http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html
    this.toggleBox = this.toggleBox.bind(this);
  }

  toggleBox() {
    // check if box is currently opened
    const { opened } = this.state;
    this.setState({
      // toggle value of `opened`
      opened: !opened,
    });
  }

  render() {
    const { title, children } = this.props;
    const { opened } = this.state;
    return (
      <div className="box">
        <div className="boxTitle" onClick={this.toggleBox}>
          {title}
        </div>
        {opened && children && (
          <div class="boxContent">
            {children}
          </div>
        )}
      </div>
    );
  }
}

ReactDOM.render((
  <ToggleBox title="Click me">
    <div>Some content</div>
  </ToggleBox>
), document.getElementById('app'));

Демо: http://jsfiddle.net/kb3gN/16688/

Я использую код как:

{opened && <SomeElement />}

Это будет отображаться, SomeElementтолько если openedэто правда. Это работает из-за способа, которым JavaScript разрешает логические условия:

true && true && 2; // will output 2
true && false && 2; // will output false
true && 'some string'; // will output 'some string'
opened && <SomeElement />; // will output SomeElement if `opened` is true, will output false otherwise

Как Reactбудет игнорировать false, я нахожу это очень хорошим способом условного рендеринга некоторых элементов.


1

Может быть, это поможет кому-то, кто сталкивается с вопросом: все условные рендеры в React Это статья обо всех различных вариантах условного рендеринга в React.

Основные выводы о том, когда использовать какой условный рендеринг:

** если еще

  • это самый основной условный рендеринг
  • начинающий дружелюбный
  • используйте if для раннего отказа от метода рендеринга, возвращая null

** троичный оператор

  • использовать его над оператором if-else
  • это более кратко, чем если-еще

** логический && оператор

  • используйте его, когда одна сторона троичной операции вернет ноль

** корпус переключателя

  • подробный
  • может быть встроено только с помощью функции самопризывания
  • избегайте этого, используйте перечисления вместо

** перечисления

  • идеально подходит для отображения различных состояний
  • идеально подходит для отображения более одного условия

** многоуровневая / вложенная условная визуализация

  • избегайте их ради читабельности
  • разделить компоненты на более легкие компоненты с их собственной простой условной визуализацией
  • использовать HOCs

** HOCs

  • использовать их, чтобы скрыть условный рендеринг
  • компоненты могут сосредоточиться на своей основной цели

** внешние шаблонные компоненты

  • избегайте их и чувствуйте себя комфортно с JSX и JavaScript

1

С ES6 вы можете сделать это с простым однострочником

const If = ({children, show}) => show ? children : null

"show" - логическое значение, и вы используете этот класс

<If show={true}> Will show </If>
<If show={false}> WON'T show </div> </If>

1
Плохой. Это всегда будет рассчитано.
Qwertiy

0

Я не думаю, что это было упомянуто. Это как твой собственный ответ, но я думаю, что это еще проще. Вы всегда можете возвращать строки из выражений и вкладывать jsx в выражения, что позволяет легко читать встроенные выражения.

render: function () {
    return (
        <div id="page">
            {this.state.banner ? <div id="banner">{this.state.banner}</div> : ''}
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

<script src="http://dragon.ak.fbcdn.net/hphotos-ak-xpf1/t39.3284-6/10574688_1565081647062540_1607884640_n.js"></script>
<script src="http://dragon.ak.fbcdn.net/hphotos-ak-xpa1/t39.3284-6/10541015_309770302547476_509859315_n.js"></script>
<script type="text/jsx;harmony=true">void function() { "use strict";

var Hello = React.createClass({
  render: function() {
    return (
      <div id="page">
        {this.props.banner ? <div id="banner">{this.props.banner}</div> : ''}
        <div id="other-content">
          blah blah blah...
        </div>
      </div>
    );   
  }
});

var element = <div><Hello /><Hello banner="banner"/></div>;
React.render(element, document.body);

}()</script>


0

Я как эксплицитность немедленно вызываемых функциональных выражений ( IIFE) и if-elseнад render callbacksи ternary operators.

render() {
  return (
    <div id="page">
      {(() => (
        const { banner } = this.state;
        if (banner) {
          return (
            <div id="banner">{banner}</div>
          );
        }
        // Default
        return (
          <div>???</div>
        );
      ))()}
      <div id="other-content">
        blah blah blah...
      </div>
    </div>
  );
}

Вам просто нужно познакомиться с IIFEсинтаксисом, {expression}это обычный синтаксис React, внутри него просто учтите, что вы пишете функцию, которая вызывает себя.

function() {

}()

которые должны быть обернуты внутри Parens

(function() {

}())

0

Существует также методика использования реквизита рендера для условного рендеринга компонента. Преимущество состоит в том, что визуализация не будет оцениваться до тех пор, пока не будет выполнено условие, что не будет беспокоиться о нулевых и неопределенных значениях.

const Conditional = ({ condition, render }) => {
  if (condition) {
    return render();
  }
  return null;
};

class App extends React.Component {
  constructor() {
    super();
    this.state = { items: null }
  }

  componentWillMount() {
    setTimeout(() => { this.setState({ items: [1,2] }) }, 2000);
  }

  render() {
    return (
      <Conditional
        condition={!!this.state.items}
        render={() => (
          <div>
            {this.state.items.map(value => <p>{value}</p>)}
          </div>
        )}
      />
    )
  }
}

0

Когда нужно что-то визуализировать, если переданное условие выполнено, вы можете использовать синтаксис:

{ condition && what_to_render }

Код таким образом будет выглядеть так:

render() {
    const { banner } = this.state;
    return (
        <div id="page">
            { banner && <div id="banner">{banner}</div> }
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

Есть, конечно, другие действительные способы сделать это, все зависит от предпочтений и поводов. В этой статье вы можете узнать больше о том, как сделать условный рендеринг в React, если вам интересно!


-1

Просто чтобы добавить еще одну опцию - если вам нравится / терпит coffee-script, вы можете использовать coffee-реагировать для написания вашего JSX, и в этом случае можно использовать операторы / else, поскольку они являются выражениями в coffee-script, а не операторами:

render: ->
  <div className="container">
    {
      if something
        <h2>Coffeescript is magic!</h2>
      else
        <h2>Coffeescript sucks!</h2>
    }
  </div>  

-1

Просто чтобы расширить @Jack Аллан ответ со ссылками на документы.

Реагировать на базовую (Quick Start) документацию предлагает null в таком случае. Однако, Booleans, Null и Undefined также игнорируются , упомянутые в Расширенном руководстве.


Исправлено, чтобы уточнить - false, null, undefined и true также могут быть проигнорированы - facebook.github.io/react/docs/… . Хотя в Quick Start они предпочитают совет нуль.
Олег В.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.