ReactJs – 创build一个“If”组件…一个好主意?

我读过React文档,“if”类​​型的语句不能在JSX代码中使用,因为JSX呈现给javascript的方式,它不会像预期的那样工作。

但是,为什么实现一个“if”组件是一个坏主意呢? 从我最初的testing看来,它似乎工作得很好,并让我想知道为什么这不是更经常做?

我的部分目的是让反应发展尽可能多,基于标记 – 尽可能less的JavaScript。 对我来说,这种方法更像是一种“数据驱动”的方法。

你可以在这里查看JS小提琴

<script type='text/javascript' src="https://unpkg.com/react@0.11.0/dist/JSXTransformer.js"></script> <script type='text/javascript' src="https://unpkg.com/react@0.11.0/dist/react-with-addons.js"></script> <script type="text/jsx"> /** @jsx React.DOM */ var If = React.createClass({ displayName: 'If', render: function() { if (this.props.condition) return <span>{this.props.children}</span> return null; } }); var Main = React.createClass({ render: function() { return ( <div> <If condition={false}> <div>Never showing false item</div> </If> <If condition={true}> <div>Showing true item</div> </If> </div> ); } }); React.renderComponent(<Main/>, document.body); </script> 

运行上述结果:

显示真实的项目

查看反应文档中JSX部分的If-Else部分。

在JSX中,你不能把语句放在大括号内 – 只有expression式。 如果你不知道expression式与JavaScript语句之间的区别,请阅读这篇文章 。 这个限制是因为JSXparsing成函数调用,并且你不能使用if语句作为JavaScript中函数的参数。 但是,您可以使用布尔运算符( &&||? : &&来执行类似的工作。 它们是expression式,所以它们可以放入JSX生成的构造函数调用中,它们的短路评估与if语句中使用的评估相同。

 <div> {(true ? <div>Showing true item</div> : <div>Never showing false item</div> )} </div> <p>My name is {this.name || "default name"}</p> 

另外,React将把nullfalse视为一个“空的组件”,它不会在真实的DOM中呈现(目前它在幕后使用相同的noscript技巧)。 当你不需要“其他”分支时,这很有用。 有关详细信息,请参阅JSX中的False 。

 <div> {shouldIncludeChild ? <ChildComponent/> : false} </div> 

至于你询问的If组件,其中一个问题就是,即使条件不成立,它也会评估其子女。 这可能会导致错误时,如果条件为真只有意义:

 <If condition={person !== null}> //This code throws an exception if this.person is null <div>{person.name}</div> </If> 

你可以通过让if组件接收主体而不是作为一个子组件列表来解决这个问题,但是它更加详细:

 <If condition={person !== null} body={function(){ return <div>{person.name}</div> }/> 

最后,由于If组件是无状态的,因此您应该考虑使用普通函数而不是新的组件类,因为这会使React的对帐algorithm变得透明。 如果你使用了一个If组件,那么一个<div>和一个<If><div>将被认为是不兼容的,React将会完成重绘,而不是试图合并新的组件。

 // This custom if function is for purely illustrative purposes // However, this idea of using callbacks to represent block of code // is useful for defining your own control flow operators in other circumstances. function myCustomIf(condition, onTrue, onFalse){ onTrue = onTrue || function(){ return null } onFalse = onFalse || function(){ return null } if(condition){ return onTrue(); }else{ return onFalse(); } } <div> {myCustomIf(person !== null, function(){ return <div>{person.name}</div> })} </div> 

你不需要任何东西比普通的JS。

安全可读(但详细)

 maybeRenderPerson: function() { var personName = ...; if ( personName ) { return <div className="person">{personName}</div>; } } render: function() { return ( <div className="component"> {this.maybeRenderPerson()} </div> ); } 

它有点冗长,但它允许轻松地在较小的,集中的块中分割你的逻辑。 当组件开始变得复杂时,这是最可读的。


简洁易读(但危险)

 render: function() { var personName = ...; // present or absent name, but never "" return ( <div className="component"> {personName && ( <div className="person">{personName}</div> )} </div> ); } 

如果被testing的variables可能是像“ 0 ""或“ false ""这样的伪造值,那么这个语法可能是相当危险的。 特别是对于数字,如果要确保它呈现为0,则应该稍微修改testing:

 render: function() { var counter= ...; // number, can be 0 return ( <div className="component"> {(typeof counter !== 'undefined') && ( <div className="counter">{counter}</div> )} </div> ); } 

当组件变得复杂时,拆分成多个更小的组件,并且使用代码风格的约定可以帮助保持它的可读性:

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

现代语法(但太早)

function无状态组件的符号可以用于表示,而不会丢失可读性。 你可以很容易地把一个大的组件分解成非常小的和集中的,使用符号:

 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}/> } 

这有点像使用JSX内部的模块模式,所以它非常灵活,但是样板less了很多。

为了实现这一点,你需要ES7阶段0编译工具,并且你最喜欢的IDE的支持可能还不存在。

你可以做这样的内联条件

 {true && ( <div>render item</div> )} {false && ( <div>don't render item</div> )} 

或者你可以只使用一个变种

 var something; if (true) { something = <div>render item</div> } {something} 

你可以做一个自我执行的function,也有正确的背景(在Babel的帮助下)! 我更喜欢这样做,因为不需要分配variables,而且您可以像想要的那样复杂(尽pipe您可能不应该为了可维护性):

 render() { return ( <div> <div>Hello!</div> {() => { if (this.props.isLoggedIn) { return <Dashboard /> } else { return <SignIn /> } }()} <div>Another Tag</div> </div> ); } 

对于这种情况,ES7实验语法非常好。 如果您使用的是Babel,请启用es7.doExpressionsfunction:

 render() { var condition = false; return ( <div> {do { if (condition) { <span>Truthy!</span>; } else { <span>Not truthy.</span>; } }} </div> ); } 

请参阅http://wiki.ecmascript.org/doku.php?id=strawman:do_expressionions

ES7封闭是伟大的:

例如,如果您使用的是webpack ,则可以添加ES7function:

首先安装stage-0function:

 npm install babel-preset-stage-0 -save-dev; 

然后在webpack加载器设置中,添加stage-0 babel预置:

  loaders : [ //... { test : /\.js$/, loader : "babel-loader", exclude : /node_modules/, query : { presets : ["react", "stage-0", "es2015"] } } ] 

那么你的组件渲染function可能如下所示:

 import React , { Component } from "react"; class ComponentWithIfs extends Component { render() { return ( <div className="my-element"> { do { if ( this.state.something ) { <div>First Case</div> } else { <div>2nd Case</div> } } } </div> ); } } 

我认为你的主要问题不是“我能否实现一个If组件”(因为显然你可以),但是“为什么实现一个If组件是没有任何理由的?

这是一个坏主意的主要原因是因为添加一个组件渲染周期,否则在现有组件中的less量代码。 额外的组件包装意味着React需要在其子项上运行整个额外的渲染周期。 如果If组件只在一个地方使用,这不是什么大问题,但是如果你的应用程序散布在他们身上,那么你的UI就会慢得多。

类似于: <div>Hi</div><div><div>Hi</div></div>更高效。

对于可维护的代码,跳过像If这样的不必要的抽象,并在render方法的return语句之前添加一些提前终止的控制逻辑,例如

 import React from 'react-native'; let { Text } = React; let Main = React.createClass({ setInitialState() { return { isShown: false } }, render() { let content; if (this.state.isShown) { content = 'Showing true item' } else { return false; } return ( <Text>{content}</Text> ); } }); 

让你干。 持久的保护。

如果有人感兴趣,我只是为了这些目的而发布一个反应模块 :

 var Node = require('react-if-comp'); ... React.createClass({ render: function() { return ( <div> <Node if={false} else={<div>Never showing false item</div>} /> <Node if={true} then={<div>Showing true item</div>} /> </div> ); } }); 

render-if是一个轻量级函数,如果条件满足,将会呈现一个元素

作为一个在线expression

 class MyComponent extends Component { render() { return ( {renderIf(1 + 2 === 3)( <span>The universe is working</span> )} ); } } 

作为一个命名函数

 class MyComponent extends Component { render() { const ifTheUniverseIsWorking = renderIf(1 + 2 === 3); return ( {ifTheUniverseIsWorking( <span>The universe is still wroking</span> )} ) } } 

作为一个组合function

 const ifEven = number => renderIf(number % 2 === 0); const ifOdd = number => renderIf(number % 2 !== 0); class MyComponent extends Component { render() { return ( {ifEven(this.props.count)( <span>{this.props.count} is even</span> )} {ifOdd(this.props.count)( <span>{this.props.count} is odd</span> )} ); } } 

对于我来说,如果不需要else语句,就可以充分利用逻辑expression式中的懒惰评估:

 render() { return( <div> { condition && <span>Displayed if condition is true</span> } </div> ); } 

或者如果需要其他的话,则是ternay操作符:

 render() { return( <div> { condition ? <span>Displayed if condition is true</span> : <span>Displayed if condition is false</span> } </div> ); } 

我会尽量避免与“模板”完全条件呈现。 在React和JSX中,您可以使用全套JavaScriptfunction来进行条件呈现 :

  • 如果别的
  • 三元
  • 逻辑&&
  • 开关箱
  • 枚举

这就是使用JSX渲染如此强大的原因。 否则,人们可以再次发明自己的模板语言,就像在Angular 1/2中一样,这是特定于库/框架的模板语言。