如何具有条件元素并保持Facebook React的JSX的DRY?


229

如何在JSX中选择性地包含元素?这是一个使用横幅的示例,如果已传递该横幅,则该横幅应位于组件中。我要避免的是必须在if语句中复制HTML标签。

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 ...
Tom Fenech

1
很好,这很有帮助。另请参见github.com/facebook/react/issues/690
Gabriel Florit

在React中进行条件渲染的选项的详尽列表:robinwieruch.de/conditional-rendering-react
Robin Wieruch

Answers:


151

只需将横幅标为未定义即可,它就不会包含在内。


1
除了答案旁边的垃圾,我什么都没有想到……好像我偶然发现了众神
Timmerz

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 />,无论条件是true还是false,始终都会执行组件内部的代码。因此,以下示例将在“ 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的情况下是这样,但确实这种方法并不完美(例如,没有很好的方法来定义else分支)。无论如何,值得一读。
tobik

1
不知道这是否仍然有效,因为最新版本似乎要求返回的值是ReactElement
srph 2015年

只需用另一个元素<span>或包装即可<div>
tobik

当我尝试使用它在<table>中呈现<td>或为空时会出现问题,因为<noscript>不能作为<tr>的子代。否则对我来说很好。
Green Su

1
如果您要执行此操作,那么有一个非常不错的编译方法:npmjs.com/package/jsx-control-statements
史蒂夫(Steve)

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.bannernullundefined,则跳过条件的右侧。


41

If样式组件是危险的,因为该代码块总是执行不管条件的。例如,如果banneris ,则将导致null异常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>
  );
}

另一种选择是使用内联函数(对else语句特别有用):

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

&& +代码样式+小组件

对于我来说,这种简单的测试语法+代码样式约定+小组件是最易读的选项。你只需要特别照顾的falsy值喜欢false0""

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

做记号

ES7的stage-0注释符号语法也非常好,当我的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-创建一个“ If”组件...一个好主意?


免费的非基本信息:第一个称为short-circuit syntax
sospedra

1
@Deerloper我猜想&&和&之间的区别是我们在计算机学校学习的第一件事,但是有些人可能不知道,因此提供更多的解释不是一个坏主意:en.wikipedia.org/wiki/Short-circuit_evaluation
Sebastien Lorber

是的,但他们将其作为经典条件语句的一部分进行了讲授。我发现很多人对于使用短路来渲染React组件感到困惑;)
sospedra

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将在第一种情况下呈现false分支,并且在逻辑合取的情况下,将不呈现任何内容。如果该属性可能为0,则只需在JSX之外使用if语句即可。


这不是JSX的问题。这是条件本身,即使在普通JS中也将表现相同。如果需要任何数字,只需将其typeof(this.state.price) === "number"用作条件(在任一变体中)。
Kroltan '16

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有人认为这些组件不利于代码阅读。但是在我看来,带有javascript的HTML更糟


像此处的“如果答案”一样,即使未显示,它仍然会执行错误条件。
基冈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

如果有React的组件,还有另一种解决方案:

var Node = require('react-if-comp');
...
render: function() {
    return (
        <div id="page">
            <Node if={this.state.banner}
                  then={<div id="banner">{this.state.banner}</div>} />
            <div id="other-content">
                blah blah blah...
            </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

还有一个非常干净的单行版本... {this.props.product.title || “无题” }

即:

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

这对我上面的示例有用吗?如果没有横幅道具,横幅div不应出现在哪里?
杰克·艾伦


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为true时才会渲染。它之所以有效,是因为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中的所有条件渲染这是一篇有关的所有不同选项的文章。

何时使用哪种条件渲染的关键要点:

** 如果别的

  • 是最基本的条件渲染
  • 初学者友好
  • 使用if是否通过返回null提前退出渲染方法

**三元运算符

  • 在if-else语句上使用它
  • 比if-else更简洁

**逻辑&&运算符

  • 当三元运算的一侧将返回null时使用它

**开关盒

  • 冗长的
  • 只能内联自调用功能
  • 避免它,使用枚举代替

**枚举

  • 完美地映射不同的状态
  • 完美地映射多个条件

**多级/嵌套条件渲染

  • 出于可读性考虑,避免使用它们
  • 使用自己的简单条件渲染将组件拆分为更轻量级的组件
  • 使用HOC

** HOC

  • 用它们来屏蔽条件渲染
  • 组件可以专注于其主要目的

**外部模板组件

  • 避免使用它们,并熟悉JSX和JavaScript

1

使用ES6,您可以使用简单的一线操作

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

“显示”是一个布尔值,您可以通过以下方式使用此类

<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-elseover 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() {

}()

需要包裹在括号内

(function() {

}())

0

还有一种使用渲染道具有条件渲染组件的技术。这样做的好处是,在满足条件之前,渲染器不会进行评估,因此无需担心nullundefined值。

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-react编写JSX,在这种情况下if / else语句是可用的,因为它们是coffee-script中的表达式,而不是语句:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.