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将把null和false视为一个“空的组件”,它不会在真实的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中一样,这是特定于库/框架的模板语言。