Comment avoir des éléments conditionnels et rester SEC avec JSX de Facebook React?

229

Comment puis-je éventuellement inclure un élément dans JSX? Voici un exemple utilisant une bannière qui devrait être dans le composant si elle a été transmise. Ce que je veux éviter, c'est d'avoir à dupliquer des balises HTML dans l'instruction if.

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>
    );
}
Jack Allan
la source
3
Et si vous n'avez pas de elsesuccursale, ça marche? Je ne connais pas jsx ...
Tom Fenech
1
Bien, cela a aidé. Voir aussi github.com/facebook/react/issues/690
Gabriel Florit
liste exhaustive des options pour effectuer un rendu conditionnel dans React: robinwieruch.de/conditional-rendering-react
Robin Wieruch

Réponses:

151

Laissez simplement la bannière non définie et elle ne sera pas incluse.

Jack Allan
la source
1
Je ne peux pas penser à autre chose qu'à la poubelle à côté de cette réponse ... comme si je suis tombé sur les dieux
Timmerz
Fonctionne-t null-il aussi bien que undefined? Est-ce aussi une partie de la spécification selon laquelle cela fonctionne de cette façon et n'est donc pas susceptible de casser à l'avenir?
hippietrail
133

Et ça. Définissons un Ifcomposant d' aide simple .

var If = React.createClass({
    render: function() {
        if (this.props.test) {
            return this.props.children;
        }
        else {
            return false;
        }
    }
});

Et utilisez-le de cette façon:

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

MISE À JOUR: Comme ma réponse devient populaire, je me sens obligé de vous avertir du plus grand danger lié à cette solution. Comme indiqué dans une autre réponse, le code à l'intérieur du <If />composant est toujours exécuté, que la condition soit vraie ou fausse. Par conséquent, l' exemple suivant échouera dans le cas où bannerest null(notez l'accès à la propriété sur la deuxième ligne):

<If test={this.state.banner}>
    <div id="banner">{this.state.banner.url}</div>
</If>

Vous devez être prudent lorsque vous l'utilisez. Je suggère de lire d'autres réponses pour des approches alternatives (plus sûres).

MISE À JOUR 2: Avec le recul, cette approche est non seulement dangereuse mais aussi désespérément lourde. C'est un exemple typique d'un développeur (moi) qui essaie de transférer des modèles et des approches qu'il connaît d'un domaine à un autre, mais cela ne fonctionne pas vraiment (dans ce cas, d'autres langages de modèles).

Si vous avez besoin d'un élément conditionnel, faites-le comme ceci:

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

Si vous avez également besoin de la branche else, utilisez simplement un opérateur ternaire:

{this.state.banner ?
   <div id="banner">{this.state.banner}</div> :
   <div>There is no banner!</div>
}

C'est beaucoup plus court, plus élégant et sûr. Je l'utilise tout le temps. Le seul inconvénient est que vous ne pouvez pas effectuer de else ifbranchement aussi facilement, mais ce n'est généralement pas si courant.

Quoi qu'il en soit, cela est possible grâce au fonctionnement des opérateurs logiques en JavaScript. Les opérateurs logiques permettent même de petites astuces comme celle-ci:

<h3>{this.state.banner.title || 'Default banner title'}</h3>
tobik
la source
1
Merci. Je recommande de lire ce github.com/facebook/react/issues/690 Ce lien a été publié dans les commentaires sous cette question et je l'ai remarqué après avoir posté ma solution. Si vous la vérifiez, un gars mentionne également cette solution mais ne la recommande pas. Personnellement, je pense que ça va, du moins dans le cas de l'OP, mais c'est vrai que cette façon n'est pas parfaite (il n'y a pas de bonne façon de définir une autre branche par exemple). Quoi qu'il en soit, cela vaut la peine d'être lu.
tobik
1
Je ne sais pas si cela fonctionne toujours car la dernière version semble exiger que la valeur renvoyée soit aReactElement
srph
Enveloppez-le simplement avec un autre élément, <span>ou <div>.
tobik
Il y a un problème lorsque j'ai essayé de l'utiliser pour rendre un <td> ou le vider dans <table>, car <noscript> n'est pas acceptable en tant qu'enfant pour <tr>. Sinon, cela fonctionne bien pour moi.
Green Su
1
Si vous allez faire cela, il existe une méthode de transpilation qui est assez bonne: npmjs.com/package/jsx-control-statements
steve
82

Personnellement, je pense vraiment que les expressions ternaires présentées dans ( JSX In Depth ) sont la manière la plus naturelle qui soit conforme aux normes ReactJs.

Voir l'exemple suivant. C'est un peu désordonné à première vue mais ça marche plutôt bien.

<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
la source
Remarque: les crochets sont identiques à un retour de composant, donc votre contenu doit être entouré d'un <div> </div>.
JoeTidee
@Chiedo pourquoi préférez-vous la réponse populaire à la place? Cela semble très bien fonctionner.
Bonhomme de neige
2
@moby Je pense que la réponse populaire est plus dynamique et se traduit par une fonction de rendu plus propre. Avec cette approche, que se passe-t-il si vous avez plusieurs conditions? Vous aurez maintenant une folie d'imbriquer avec ce format étrange et si deux zones distinctes nécessitent les mêmes changements en fonction du conditionnel, vous devez maintenant réécrire le conditionnel à nouveau. Mais mon avis! :)
Chiedo
une page connexe est également très utile: facebook.github.io/react/docs/…
Neil
46

Vous pouvez aussi l'écrire comme

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

Si votre state.bannerest nullou undefined, le côté droit de la condition est ignoré.

wialy
la source
41

Le Ifcomposant de style est dangereux car le bloc de code est toujours exécuté quelle que soit la condition. Par exemple, cela provoquerait une exception nulle si bannerest null:

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

Une autre option consiste à utiliser une fonction en ligne (particulièrement utile avec les instructions 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>
  );
}

Une autre option parmi les problèmes de réaction :

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>
  );
}
bendytree
la source
2
De toutes les solutions jusqu'à présent, la fonction en ligne semble la plus élégante et la plus simple. Quelque chose à surveiller?
suryasankar du
22

&& + style de code + petits composants

Cette syntaxe de test simple + convention de style de code + petits composants ciblés est pour moi l'option la plus lisible. Vous avez juste besoin de faire particulièrement attention aux valeurs de falsification comme false, 0ou "".

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

faire de la notation

La syntaxe de notation ES7 stage-0 do est également très agréable et je l'utiliserai définitivement lorsque mon IDE la prendra en charge correctement:

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

Plus de détails ici: ReactJs - Créer un composant "If" ... une bonne idée?

Sébastien Lorber
la source
Informations non basiques gratuites: la première s'appelleshort-circuit syntax
sospedra
1
@Deerloper Je suppose que la différence entre && et & est l'une des premières choses que nous apprenons dans les écoles d'informatique, mais certaines personnes ne le savent pas, ce n'est donc pas une mauvaise idée de donner plus d'explications: en.wikipedia.org/wiki/Short-circuit_evaluation
Sebastien Lorber
Certes, mais ils l'enseignent dans le cadre des déclarations conditionnelles classiques. J'ai trouvé beaucoup de gens confus quant à l'utilisation du court-circuit pour rendre un composant React;)
sospedra
22

Simple, créez une fonction.

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

C'est un schéma que je suis personnellement tout le temps. Rend le code vraiment propre et facile à comprendre. De plus, il vous permet de refactoriser Bannerson propre composant s'il devient trop volumineux (ou réutilisé dans d'autres endroits).

Michael Yagudaev
la source
13

La dosyntaxe expérimentale ES7 rend cela facile. Si vous utilisez Babel, activez la es7.doExpressionsfonction puis:

render() {
  return (
    <div id="banner">
      {do {
        if (this.state.banner) {
          this.state.banner;
        } else {
          "Something else";
        }
      }}
    </div>
  );
}

Voir http://wiki.ecmascript.org/doku.php?id=strawman:do_expressions

balexand
la source
11

Comme déjà mentionné dans les réponses, JSX vous présente deux options

  • Opérateur ternaire

    { this.state.price ? <div>{this.state.price}</div> : null }

  • Conjonction logique

    { this.state.price && <div>{this.state.price}</div> }


Cependant, ceux-ci ne fonctionnent pas price == 0 .

JSX rendra la fausse branche dans le premier cas et en cas de conjonction logique, rien ne sera rendu. Si la propriété peut être 0, utilisez simplement les instructions if en dehors de votre JSX.

Lyubomir
la source
Ce n'est pas un problème JSX. C'est la condition elle-même, même en clair JS se comporterait de la même manière. Si vous voulez un nombre, utilisez-le typeof(this.state.price) === "number"comme condition (dans l'une ou l'autre variante).
Kroltan
8

Ce composant fonctionne lorsque vous avez plusieurs éléments dans la branche "if":

var Display = React.createClass({
  render: function () {
    if (!this.props.when) {
      return false;
    }
    return React.DOM.div(null, this.props.children);
  },
});

Usage:

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 quelqu'un pense que ces composants ne sont pas bons pour la lecture de code. Mais dans mon esprit, le HTML avec javascript est pire

k.makarov
la source
Comme la réponse If ici, cela exécutera toujours la fausse condition même si elle n'est pas affichée.
Keegan 82 du
3

La plupart des exemples sont avec une ligne de "html" qui est rendue conditionnellement. Cela me semble lisible lorsque j'ai plusieurs lignes qui doivent être rendues conditionnellement.

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

Le premier exemple est bon car au lieu de nullnous pouvons conditionnellement rendre un autre contenu comme{this.props.showContent ? content : otherContent}

Mais si vous avez juste besoin d'afficher / masquer le contenu, c'est encore mieux car les booléens, Null et Undefined sont ignorés

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>
  );
}
ivn
la source
2

Il existe une autre solution, si composant pour 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>
    );
}
Gordon Freeman
la source
2

J'utilise un raccourci plus explicite: Une expression de fonction immédiatement invoquée (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})}/>
  }
})()}
jsdario
la source
1

J'ai fait https://www.npmjs.com/package/jsx-control-statements pour le rendre un peu plus facile, au fond , il vous permet de définir <If>conditionals sous forme de balises et les compile ensuite dans ifs ternaires afin que le code à l' intérieur du <If>seul obtient exécuté si la condition est vraie.

Alex Gilleran
la source
1

Il existe également une version en ligne très propre ... {this.props.product.title || "Pas de titre" }

C'est à dire:

render: function() {
            return (
                <div className="title">
                    { this.props.product.title || "No Title" }
                </div>
            );
        }
max kaplan
la source
Est-ce que cela fonctionne avec mon échantillon ci-dessus? Où la bannière ne doit-elle pas apparaître s'il n'y a pas d'accessoire de bannière?
Jack Allan
1

J'ai récemment créé https://github.com/ajwhite/render-if pour rendre les éléments en toute sécurité uniquement si le prédicat passe .

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

ou

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

//...

{ifUniverseIsWorking(
  <span>Hello!</span>
)}
Atticus
la source
1

Vous pouvez conditionnellement inclure des éléments à l'aide de l'opérateur ternaire comme ceci:

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>
}
mada-g
la source
1

Vous pouvez utiliser une fonction et renvoyer le composant et réduire la fonction de rendu

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 />
    }
  }
}
pedronalbert
la source
1

Voici mon approche en utilisant 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'));

Démo: http://jsfiddle.net/kb3gN/16688/

J'utilise du code comme:

{opened && <SomeElement />}

Cela ne sera rendu SomeElementque si openedc'est vrai. Cela fonctionne en raison de la façon dont JavaScript résout les conditions logiques:

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

Comme Reactje l'ignorerai false, je trouve que c'est un très bon moyen de rendre conditionnellement certains éléments.

pie6k
la source
1

Peut-être que cela aide quelqu'un qui tombe sur la question: tous les rendus conditionnels dans React C'est un article sur toutes les différentes options de rendu conditionnel dans React.

Points clés à retenir pour savoir quel rendu conditionnel:

** sinon

  • est le rendu conditionnel le plus basique
  • adapté aux débutants
  • utilisez if pour vous désinscrire tôt d'une méthode de rendu en renvoyant null

** opérateur ternaire

  • l'utiliser sur une instruction if-else
  • c'est plus concis que if-else

** opérateur && logique

  • l'utiliser lorsqu'un côté de l'opération ternaire retournerait nul

** boîtier de commutateur

  • verbeux
  • ne peut être aligné qu'avec une fonction auto-invoquante
  • évitez-le, utilisez plutôt des énumérations

** énumérations

  • parfait pour cartographier différents états
  • parfait pour cartographier plusieurs conditions

** rendus conditionnels à plusieurs niveaux / imbriqués

  • les éviter pour plus de lisibilité
  • diviser les composants en composants plus légers avec leur propre rendu conditionnel simple
  • utiliser des HOC

** HOC

  • les utiliser pour masquer le rendu conditionnel
  • les composants peuvent se concentrer sur leur objectif principal

** composants de modèles externes

  • évitez-les et soyez à l'aise avec JSX et JavaScript
Robin Wieruch
la source
1

Avec ES6, vous pouvez le faire avec une simple doublure

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

"show" est un booléen et vous utilisez cette classe par

<If show={true}> Will show </If>
<If show={false}> WON'T show </div> </If>
Léon
la source
1
Mauvais. Il sera toujours calculé.
Qwertiy
0

Je ne pense pas que cela ait été mentionné. C'est comme votre propre réponse, mais je pense que c'est encore plus simple. Vous pouvez toujours renvoyer des chaînes à partir des expressions et vous pouvez imbriquer jsx à l'intérieur des expressions, ce qui permet une expression en ligne facile à lire.

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>

Juan Mendes
la source
0

Je aime l'explicitation des expressions de fonction Immédiatement-(OPPOSABLES AU IIFE) et if-elseplus render callbackset 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>
  );
}

Vous avez juste besoin de vous familiariser avec la IIFEsyntaxe, {expression}c'est la syntaxe React habituelle, à l'intérieur il suffit de considérer que vous écrivez une fonction qui s'appelle elle-même.

function() {

}()

qui doivent être enveloppés dans des parens

(function() {

}())
Gianluca Esposito
la source
0

Il existe également une technique utilisant des accessoires de rendu pour effectuer un rendu conditionnel d'un composant. Son avantage est que le rendu n'est pas évalué tant que la condition n'est pas remplie, ce qui n'entraîne aucun souci pour les valeurs nulles et non définies .

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>
        )}
      />
    )
  }
}
Farzad YZ
la source
0

Lorsque vous ne devez rendre quelque chose que si la condition passée est remplie, vous pouvez utiliser la syntaxe:

{ condition && what_to_render }

Le code de cette manière ressemblerait à ceci:

render() {
    const { banner } = this.state;
    return (
        <div id="page">
            { banner && <div id="banner">{banner}</div> }
            <div id="other-content">
                blah blah blah...
            </div>
        </div>
    );
}

Il y a, bien sûr, d'autres façons valables de le faire, tout dépend des préférences et de l'occasion. Vous pouvez en savoir plus sur la façon de faire un rendu conditionnel dans React dans cet article si vous êtes intéressé!

Nesha Zoric
la source
-1

Juste pour ajouter une autre option - si vous aimez / tolérez coffee-script, vous pouvez utiliser coffee-react pour écrire votre JSX, auquel cas si les instructions / else sont utilisables car ce sont des expressions dans coffee-script et non des instructions:

render: ->
  <div className="container">
    {
      if something
        <h2>Coffeescript is magic!</h2>
      else
        <h2>Coffeescript sucks!</h2>
    }
  </div>  
Hal
la source
-1

Juste pour étendre la réponse de @Jack Allan avec des références aux documents.

La documentation de base de React (Démarrage rapide) suggère null dans ce cas. Cependant, les booléens, Null et non définis sont également ignorés , mentionnés dans le guide avancé.

Oleg V.
la source
Corrigé pour clarifier - faux, nul, non défini et vrai sont également valables pour être ignorés - facebook.github.io/react/docs/… . Bien que, dans Quick Start, ils préfèrent conseiller null.
Oleg V.