如何有条件的元素,并保持DRY与Facebook React的JSX?
如何在JSX中select性地包含元素? 下面是一个使用横幅的例子,如果它已经被传入,那么我想避免的是在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> ); }
只是把横幅作为未定义,并不包括在内。
那这个呢。 我们来定义一个简单的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> ); }
更新:由于我的回答越来越受欢迎,我觉得有义务警告您与此解决scheme有关的最大危险。 正如在另一个答案中指出的那样,无论条件是真还是假, <If />
组件中的代码总是被执行。 因此,如果banner
为null
,下面的示例将失败(请注意第二行的属性访问):
<If test={this.state.banner}> <div id="banner">{this.state.banner.url}</div> </If>
使用时请注意。 我build议阅读其他答案替代(更安全)的方法。
更新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
分支很容易,但是通常不那么常见,那么你不能做else if
。
无论如何,这是可能的,这要归功于JavaScript中的逻辑运算符的工作原理。 逻辑运算符甚至允许这样的小技巧:
<h3>{this.state.banner.title || 'Default banner title'}</h3>
就我个人而言,我真的认为在http://facebook.github.io/react/tips/if-else-in-JSX.html中显示的三元expression式是符合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>
If
样式组件是危险的,因为无论条件如何,代码块总是被执行 。 例如,如果banner
为null
,则会导致空的exception:
//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> ); }
另一个select是使用内联函数(对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> ); }
你也可以这样写
{ this.state.banner && <div>{...}</div> }
如果你的state.banner
是null
或者undefined
,则跳过条件的右边。
&& +代码风格+小型组件
这个简单的testing语法+代码风格约定+小集中的组件对我来说是最可读的选项。 你只需要特别注意像false
, 0
或""
false
值。
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 – 创build一个“如果”组件…一个好主意?
简单,创build一个function。
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
它自己的组件。
实验性的ES7语法使得这一点变得简单。 如果您使用的是Babel,请启用es7.doExpressions
function:
render() { return ( <div id="banner"> {do { if (this.state.banner) { this.state.banner; } else { "Something else"; } }} </div> ); }
请参阅http://wiki.ecmascript.org/doku.php?id=strawman:do_expressionions
正如答案中已经提到的,JSX提供了两个选项
-
三元操作符
{ this.state.price ? <div>{this.state.price}</div> : null }
-
逻辑连接
{ this.state.price && <div>{this.state.price}</div> }
但是,那些不工作的 price == 0
。
JSX将在第一种情况下呈现假分支,如果是逻辑连接,则不会呈现任何内容。 如果该属性可能为0,则只需在JSX之外使用if语句。
当“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是更糟的
大多数例子都是有条件地呈现的一行“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}
{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> ); }
还有另一个解决scheme, 如果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> ); }
我使用更明确的快捷方式:立即调用函数expression式(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})}/> } })()}
我做了https://www.npmjs.com/package/jsx-control-statements使它更容易一些,基本上它允许你定义;<If>
条件作为标签,然后将它们编译成三元ifs,这样代码里面如果条件为真, <If>
才会被执行。
我最近做了https://github.com/ajwhite/render-if , 只有在谓词通过的情况下才能安全地呈现元素。
{renderIf(1 + 1 === 2)( <span>Hello!</span> )}
要么
const ifUniverseIsWorking = renderIf(1 + 1 === 2); //... {ifUniverseIsWorking( <span>Hello!</span> )}
你可以像下面这样使用三元运算符有条件地包含元素:
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> }
您可以使用一个函数并返回组件并保持细化渲染function
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 /> } } }
也许它可以帮助遇到问题的人: React中的所有条件呈现这是一篇关于React中条件呈现的所有不同选项的文章。
关于何时使用哪个条件呈现的关键要点:
** 如果别的
- 是最基本的条件渲染
- 初学者友好
- 使用是否通过返回null来提前退出渲染方法
**三元操作符
- 通过if-else语句使用它
- 它比if-else更简洁
**逻辑&&运算符
- 当三元操作的一边将返回null时使用它
**开关箱
- 详细
- 只能用自我调用function来内联
- 避免它,而不是使用枚举
**枚举
- 完美映射不同的状态
- 完美映射多个条件
**多级/嵌套的条件渲染
- 为了可读性避免它们
- 用自己简单的条件渲染将组件分解为更轻量级的组件
- 使用HOC
** HOC
- 使用它们来屏蔽掉有条件的渲染
- 组件可以专注于其主要目的
**外部模板组件
- 避免使用JSX和JavaScript
还有一个非常干净的一行版本… {this.props.product.title || “无题” }
即:
render: function() { return ( <div className="title"> { this.props.product.title || "No Title" } </div> ); }
我不认为这已经提到。 这就像你自己的答案,但我认为它更简单。 您可以始终从expression式中返回string,并且可以在expression式中嵌套jsx,因此可以轻松地读取内联expression式。
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>
我喜欢立即调用函数expression式( IIFE
)的明确性,以及if-else
over 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() { }())
这是我使用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 />}
只有opened
才会呈现SomeElement
。 这是因为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
,所以我觉得这是条件渲染某些元素的好方法。
只要添加另一个选项 – 如果您喜欢/容忍咖啡脚本,您可以使用咖啡反应来编写您的JSX,在这种情况下,if / else语句可用,因为它们是coffee-script中的expression式,而不是语句:
render: -> <div className="container"> { if something <h2>Coffeescript is magic!</h2> else <h2>Coffeescript sucks!</h2> } </div>
只是扩大@Jack Allan的答案与文档的参考。
在这种情况下, 反应基本(Quick Start)文档build议为 null
。 但是,“高级指南”中提到的布尔值,空值和未定义值也被忽略 。