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.doExpressions
function:
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-0
function:
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中一样,这是特定于库/框架的模板语言。