web-dev-qa-db-fra.com

Comment avoir des éléments conditionnels et garder DRY avec le JSX de Facebook React?

Comment puis-je éventuellement inclure un élément dans JSX? Voici un exemple d'utilisation d'une bannière qui devrait figurer dans le composant s'il a été passé. Ce que je veux éviter, c'est d'avoir à dupliquer les 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>
    );
}
223
Jack Allan

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

147
Jack Allan

Et ça. Définissons un simple composant If

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

UPDATE: Ma réponse étant de plus en plus populaire, je me sens obligé de vous avertir du danger le plus important lié à cette solution. Comme indiqué dans une autre réponse, le code contenu dans le composant <If /> est toujours exécuté, que la condition soit vraie ou fausse. Par conséquent, le exemple suivant échouera si la banner est null (notez l'accès de la propriété sur la deuxième ligne):

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

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

UPDATE 2: Rétrospectivement, cette approche est non seulement dangereuse, mais également extrêmement lourde. C'est un exemple typique lorsqu'un développeur (moi) essaie de transférer des modèles et des approches qu'il connaît d'un domaine à un autre mais que cela ne fonctionne pas vraiment (dans ce cas, d'autres langages de modèle).

Si vous avez besoin d'un élément conditionnel, procédez comme suit:

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 plus sûr. Je l'utilise tout le temps. Le seul inconvénient est que vous ne pouvez pas créer de branche else if aussi facilement, mais ce n’est généralement pas aussi courant.

Quoi qu'il en soit, cela est possible grâce au fonctionnement de 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>
129
tobik

Personnellement, je pense vraiment que les expressions ternaires apparaissent dans http://facebook.github.io/react/tips/if-else-in-JSX.html sont la manière la plus naturelle de se conformer aux normes de ReactJs.

Voir l'exemple suivant. C'est un peu brouillon au premier abord mais ça fonctionne 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>
81
Chiedo

Vous pouvez aussi l'écrire comme 

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

Si votre state.banner est null ou undefined, le côté droit de la condition est ignoré.

44
wialy

Le composant de style If est dangereux car le bloc de code est toujours exécuté quelle que soit la condition. Par exemple, cela provoquerait une exception NULL si banner est 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 inline (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 de réaction questions :

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

&& + code-style + 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 devez juste prendre soin des valeurs de fausseté telles que false, 0 ou "".

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

faire la notation

La syntaxe de notation de l'étape 0 de l'ES7 est également très agréable et je l'utilisera définitivement lorsque mon IDE la supportera 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 "Si" ... une bonne idée?

22

Simple, créer 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 modèle que je suis personnellement tout le temps. Rend le code vraiment propre et facile à comprendre. De plus, il vous permet de refactoriser Banner dans son propre composant s'il devient trop volumineux (ou réutilisé ailleurs).

21
Michael Yagudaev

La syntaxe expérimentale ES7 do facilite cette opération. Si vous utilisez Babel, activez la fonction es7.doExpressions 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

13
balexand

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 pour 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 uniquement des instructions if en dehors de votre JSX.

11
Lyubomir

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

P.s. quelqu'un pense que ces composants ne sont pas bons pour la lecture de code. Mais dans mon esprit, le langage HTML avec javascript est pire 

8
k.makarov

La plupart des exemples concernent une ligne de "html" rendue de manière conditionnelle. Cela me semble lisible lorsque plusieurs lignes doivent être restituées de manière conditionnelle.

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 null, nous pouvons restituer sous forme conditionnelle un autre contenu comme {this.props.showContent ? content : otherContent}

Mais si vous avez juste besoin d'afficher/masquer du contenu, c'est encore mieux puisque les booléens, les valeurs Null et les indéfinis 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>
  );
}
3
ivn

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

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>
    );
}
2
Gordon Freeman

Il existe également une version d'une ligne vraiment propre ... {this.props.product.title || "Pas de titre" }

C'est à dire:

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

Voici mon approche 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 un code comme:

{opened && <SomeElement />}

Cela rendra SomeElement uniquement si opened est vrai. Cela fonctionne à cause 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 React ignorera false, je trouve que c'est un très bon moyen de restituer certains éléments sous condition.

1
pie6k

Vous pouvez inclure conditionnellement des éléments en utilisant 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>
}
1
mada-g

J'ai créé https://www.npmjs.com/package/jsx-control-statements pour que ce soit un peu plus simple, cela permet de définir les variables conditionnelles <If> comme des balises, puis de les compiler en if Le code à l'intérieur de <If> n'est exécuté que si la condition est vraie.

1
Alex Gilleran

Cela aide peut-être quelqu'un qui tombe sur la question: Tous les rendus conditionnels dans React C'est un article sur les différentes options de rendu conditionnel dans React. 

Principaux points à retenir du moment d'utiliser quel rendu conditionnel:

** sinon

  • est le rendu conditionnel le plus élémentaire
  • débutant amical
  • utiliser si désactiver tôt une méthode de rendu en renvoyant null

** opérateur ternaire

  • utilisez-le sur une déclaration if-else
  • c'est plus concis que si-sinon

** opérateur logique &&

  • utilisez-le quand un côté de l'opération ternaire renverrait null

** boîtier de commutation

  • verbeux
  • ne peut être intégré qu'avec la fonction d'invocation automatique
  • évitez-le, utilisez plutôt des enums

** enums

  • parfait pour cartographier différents états
  • parfait pour cartographier plus d'une condition

** rendus conditionnels multi-niveaux/imbriqués

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

** COH

  • utilisez-les pour protéger le rendu conditionnel
  • les composants peuvent se concentrer sur leur objectif principal

** composants de templates externes

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

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

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

ou

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

//...

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

Vous pouvez utiliser une fonction et retourner le composant tout en conservant 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 />
    }
  }
}
1
pedronalbert

Avec ES6, vous pouvez le faire avec un simple liner

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>
1
Leon

Il existe également une technique utilisant render props pour rendre conditionnellement un composant. Son avantage est que le rendu ne sera pas évalué tant que la condition n'est pas remplie, ce qui ne pose aucun problème pour les valeurs null et undefined.

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

Je ne pense pas que cela a été mentionné. Cela ressemble à votre propre réponse mais je pense que c'est encore plus simple. Vous pouvez toujours renvoyer des chaînes à partir d'expressions et vous pouvez imbriquer des expressions jsx dans des expressions, ce qui permet une expression 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>

0
Juan Mendes

Lorsque vous ne devez restituer 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 existe bien sûr d'autres moyens valables de le faire, tout dépend des préférences et de l'occassion. Vous pouvez en apprendre davantage sur la manière de rendre le rendu conditionnel dans React dans cet article si cela vous intéresse!

0
Nesha Zoric

J'aime le caractère explicite des expressions de fonction appelées immédiatement (IIFE) et de if-else sur render callbacks et 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>
  );
}

Il vous suffit de vous familiariser avec la syntaxe IIFE, {expression} étant la syntaxe habituelle de React. À l'intérieur, considérez que vous écrivez une fonction qui s'appelle elle-même.

function() {

}()

qui doivent être enveloppés à l'intérieur des parens

(function() {

}())
0
Gianluca Esposito