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

Posted

技术标签:

【中文标题】如何使用 Facebook React 的 JSX 拥有条件元素并保持 DRY?【英文标题】:How to have conditional elements and keep DRY with Facebook React's JSX? 【发布时间】:2014-04-27 15:11:02 【问题描述】:

如何在 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>
    );

【问题讨论】:

如果您只是没有else 分支怎么办,这行得通吗?我对jsx不熟悉... 很好,这有帮助。另见github.com/facebook/react/issues/690 在 React 中进行条件渲染的详尽选项列表:robinwieruch.de/conditional-rendering-react 【参考方案1】:

将横幅保留为未定义,它不会被包含在内。

【讨论】:

我想不出除了那个答案旁边的垃圾......好像我偶然发现了众神 null 是否和undefined 一样好用?规范的那部分是否也是这样工作的,因此将来不太可能中断?【参考方案2】:

这个呢。让我们定义一个简单的帮助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>
    );

更新:随着我的回答越来越受欢迎,我觉得有义务警告您与此解决方案相关的最大危险。正如另一个答案中所指出的,&lt;If /&gt; 组件内的代码始终执行,无论条件是真还是假。因此,如果bannernull,则以下example 将失败(注意第二行的属性访问):

<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 中 logical operators 的工作方式。逻辑运算符甚至允许像这样的小技巧:

<h3>this.state.banner.title || 'Default banner title'</h3>

【讨论】:

谢谢。我建议阅读此github.com/facebook/react/issues/690 此链接已发布在此问题下方的 cmets 中,我在发布解决方案后注意到了它。如果你检查它,有些人也会提到这个解决方案,但不推荐它。我个人认为没关系,至少在 OP 的情况下,但确实这种方式并不完美(例如,没有定义 else 分支的好方法)。总之,值得一读。 不确定这是否仍然有效,因为最新版本似乎要求返回值是ReactElement 用另一个元素包裹它,&lt;span&gt;&lt;div&gt; 当我尝试使用它来渲染 或 中的空时出现问题,因为 的子级。否则它对我来说很好。 如果你要这么做的话,有一个转译方法很不错:npmjs.com/package/jsx-control-statements
【参考方案3】:

就个人而言,我真的认为 (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>

【讨论】:

注意:方括号和组件返回一样,所以你的内容需要用括起来。 @Chiedo 为什么您更喜欢流行的答案?这似乎效果很好。 @moby 我认为流行的答案更具动态性,并产生更清晰的渲染功能。使用这种方法,如果您有多个条件会发生什么?您现在会疯狂地使用这种奇怪的格式进行嵌套,如果两个单独的区域需要基于条件进行相同的更改,您现在必须再次重写条件。虽然只是我的意见! :) 相关页面也挺有用的:facebook.github.io/react/docs/…【参考方案4】:

你也可以这样写

 this.state.banner && <div>...</div> 

如果您的state.bannernullundefined,则跳过右侧的条件。

【讨论】:

【参考方案5】:

If 样式组件很危险,因为无论条件如何,代码块总是执行。例如,如果bannernull,这将导致空异常:

//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>
  );

react issues 的另一个选项:

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>
  );

【讨论】:

在迄今为止的所有解决方案中,内联函数似乎是最优雅和最直接的。有什么需要注意的吗?【参考方案6】:

简单,创建一个函数。

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 变得太大(或在其他地方重用),它还允许您将其重构为自己的组件。

【讨论】:

【参考方案7】:

&& + 代码风格 + 小组件

这种简单的测试语法 + 代码风格的约定 + 专注的小组件对我来说是最易读的选项。您只需要特别注意 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 do notation 语法也非常好,当我的 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 - Creating an "If" component... a good idea?

【讨论】:

免费非基本信息:第一个叫short-circuit syntax @Deerloper 我猜 && 和 & 之间的区别是我们在计算机学校学习的第一件事,但有些人可能不知道,所以给出更多解释也不错:en.wikipedia.org/wiki/Short-circuit_evaluation 没错,但他们将其作为经典条件语句的一部分来教授。我发现很多人对使用短路来渲染 React 组件感到困惑;)【参考方案8】:

实验性的 ES7 do 语法使这很容易。如果您使用 Babel,请启用 es7.doExpressions 功能:

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_expressions

【讨论】:

【参考方案9】:

正如答案中已经提到的,JSX 为您提供了两种选择

三元运算符

this.state.price ? &lt;div&gt;this.state.price&lt;/div&gt; : null

逻辑连词

this.state.price &amp;&amp; &lt;div&gt;this.state.price&lt;/div&gt;


但是,这些不适用于 price == 0

JSX 将在第一种情况下呈现假分支,在逻辑合取的情况下,不会呈现任何内容。如果属性可能为 0,只需在 JSX 之外使用 if 语句。

【讨论】:

这不是 JSX 问题。这是条件本身,即使在普通的 JS 中也会表现相同。如果您想要任何数字,只需使用 typeof(this.state.price) === "number" 作为条件(在任一变体中)。【参考方案10】:

当您在“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>
  );

附:有人认为这些组件不利于代码阅读。但在我看来,带有 javascript 的 Html 更糟糕

【讨论】:

就像这里的 If 答案一样,即使没有显示出来,它仍然会执行 false 条件。 这是一个很好的小助手组件,它可以保持代码的简洁和易于使用。它不是总是创建新函数来渲染某些位,而是保持代码干净且易于阅读【参考方案11】:

大多数示例都是有条件呈现的一行“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

但如果您只需要显示/隐藏内容,这会更好,因为Booleans, Null, and Undefined Are Ignored

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>
  );

【讨论】:

【参考方案12】:

还有另一种解决方案,if component for 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>
    );

【讨论】:

【参考方案13】:

我使用更明确的快捷方式:立即调用函数表达式 (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)/>
  
)()

【讨论】:

【参考方案14】:

也许它可以帮助遇到这个问题的人:All the Conditional Renderings in React 这是一篇关于 React 中条件渲染的所有不同选项的文章。

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

** if-else

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

** 三元运算符

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

** 逻辑 && 运算符

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

** 开关盒

冗长 只能内联自调用函数 避免它,改用枚举

** 枚举

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

** 多级/嵌套条件渲染

为了可读性而避免使用它们 使用自己的简单条件渲染将组件拆分为更轻量级的组件 使用 HOC

** HOC

使用它们来屏蔽条件渲染 组件可以专注于其主要用途

** 外部模板组件

避开它们,熟悉 JSX 和 JavaScript

【讨论】:

【参考方案15】:

我做了https://www.npmjs.com/package/jsx-control-statements 使它更容易一点,基本上它允许您将&lt;If&gt; 条件定义为标签,然后将它们编译成三元ifs,以便&lt;If&gt; 中的代码只有在条件为时才会被执行真的。

【讨论】:

【参考方案16】:

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

即:

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

【讨论】:

这适用于我上面的示例吗?如果没有banner prop,banner div不应该出现在哪里?【参考方案17】:

我最近创建了https://github.com/ajwhite/render-if 以安全地渲染元素只有在谓词通过时

renderIf(1 + 1 === 2)(
  <span>Hello!</span>
)

const ifUniverseIsWorking = renderIf(1 + 1 === 2);

//...

ifUniverseIsWorking(
  <span>Hello!</span>
)

【讨论】:

【参考方案18】:

您可以使用三元运算符有条件地包含元素,如下所示:

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>

【讨论】:

【参考方案19】:

您可以使用函数并返回组件并保持渲染函数的细化

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

【讨论】:

【参考方案20】:

这是我使用 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,我发现它是有条件地渲染某些元素的好方法。

【讨论】:

【参考方案21】:

在 ES6 中,你可以用一个简单的单行代码来做到这一点

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

“show”是一个布尔值,你使用这个类

<If show=true> Will show </If>
<If show=false> WON'T show </div> </If>

【讨论】:

不好。它总是会被计算出来的。【参考方案22】:

我认为没有人提到这一点。这就像您自己的答案,但我认为它更简单。您总是可以从表达式中返回字符串,并且可以将 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>

【讨论】:

【参考方案23】:

我喜欢立即调用函数表达式 (IIFE) 和 if-else 优于 render callbacksternary 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() 

())

【讨论】:

【参考方案24】:

还有一种技术使用 render props 来条件渲染组件。这样做的好处是在满足条件之前渲染不会进行评估,从而无需担心 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>
        )
      />
    )
  

【讨论】:

【参考方案25】:

如果必须仅在满足传递的条件时渲染某些内容,您可以使用语法:

 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>
    );

当然,还有其他有效的方法可以做到这一点,这完全取决于偏好和场合。如果您有兴趣,可以在this article 中了解更多关于如何在 React 中进行条件渲染的方法!

【讨论】:

【参考方案26】:

我只是在 React with TypeScript 中使用以下 sn-p

export default function ReactIf(props: condition: boolean, children: React.ReactNode ) 
    return props.condition ? <React.Fragment>props.children</React.Fragment> : <React.Fragment/>;


【讨论】:

【参考方案27】:

只是添加另一个选项 - 如果你喜欢/容忍咖啡脚本,你可以使用咖啡反应来编写你的 JSX,在这种情况下 if/else 语句是可用的,因为它们是咖啡脚本中的表达式而不是语句:

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

【讨论】:

【参考方案28】:

只是通过引用文档来扩展@Jack Allan 的答案。

React basic (Quick Start) documentation suggests null 在这种情况下。 但是,Booleans, Null, and Undefined Are Ignored 也一样,在高级指南中提到。

【讨论】:

已更正以澄清 -- false、null、undefined 和 true 也可以忽略 -- facebook.github.io/react/docs/…。虽然,在快速入门中,他们更喜欢建议 null。

以上是关于如何使用 Facebook React 的 JSX 拥有条件元素并保持 DRY?的主要内容,如果未能解决你的问题,请参考以下文章

[React 基础系列] 什么是 JSX,以及如何使用 JSX

让 Facebook 的 react.js 库 JSX 语法与 jslint 完美搭配?

前端库-React框架:「01] 创建一个简单的 JSX 元素

react简述-react基础-jsx语法-jsx表达式-jsx动态属性-jsx列表渲染

React的JSX语言

React的JSX语言