Como ter elementos condicionais e manter DRY com o JSX do Facebook React?

229

Como opcionalmente incluo um elemento no JSX? Aqui está um exemplo usando um banner que deve estar no componente se ele foi passado. O que eu quero evitar é ter que duplicar as tags HTML na instrução 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
fonte
3
E se você simplesmente não tiver uma elsefilial, isso funciona? Eu não estou familiarizado com JSX ...
Tom Fenech
1
Bom, isso ajudou. Veja também github.com/facebook/react/issues/690
Gabriel Florit
lista exaustiva de opções para fazer a renderização condicional em Reagir: robinwieruch.de/conditional-rendering-react
Robin Wieruch

Respostas:

151

Apenas deixe o banner como indefinido e ele não será incluído.

Jack Allan
fonte
1
Eu não consigo pensar em nada, mas o lixo ao lado dessa resposta ... como se me deparei com os deuses
Timmerz
Funciona nulltão bem quanto undefined? Também é parte da especificação que funciona dessa maneira e, portanto, provavelmente não será interrompida no futuro?
Hippietrail 08/09/19
133

Que tal isso? Vamos definir um Ifcomponente de ajuda simples .

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

E use-o desta maneira:

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

ATUALIZAÇÃO: Como minha resposta está ficando popular, sinto-me obrigado a avisá-lo sobre o maior perigo relacionado a esta solução. Conforme apontado em outra resposta, o código dentro do <If />componente é executado sempre, independentemente de a condição ser verdadeira ou falsa. Portanto, o exemplo a seguir falhará no caso de banneris null(observe o acesso à propriedade na segunda linha):

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

Você precisa ter cuidado ao usá-lo. Sugiro a leitura de outras respostas para abordagens alternativas (mais seguras).

ATUALIZAÇÃO 2: Olhando para trás, essa abordagem não é apenas perigosa, mas também desesperadamente complicada. É um exemplo típico de quando um desenvolvedor (eu) tenta transferir padrões e abordagens que conhece de uma área para outra, mas na verdade não funciona (nesse caso, outras linguagens de modelos).

Se você precisar de um elemento condicional, faça o seguinte:

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

Se você também precisar da ramificação else, basta usar um operador ternário:

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

É muito mais curto, mais elegante e seguro. Eu uso isso o tempo todo. A única desvantagem é que você não pode fazer else iframificações tão facilmente, mas isso geralmente não é tão comum.

De qualquer forma, isso é possível graças ao funcionamento dos operadores lógicos no JavaScript. Os operadores lógicos até permitem pequenos truques como este:

<h3>{this.state.banner.title || 'Default banner title'}</h3>
tobik
fonte
1
Obrigado. Eu recomendo a leitura deste github.com/facebook/react/issues/690 Este link foi postado nos comentários abaixo desta pergunta e eu a notei depois de postar minha solução. Se você verificá-lo, alguém menciona essa solução também, mas não a recomenda. Pessoalmente, acho que está tudo bem, pelo menos no caso do OP, mas é verdade que esse caminho não é perfeito (não há uma maneira legal de definir outro ramo, por exemplo). Enfim, vale a pena ler.
Tobik 9/10/2014
1
Não tenho certeza se isso ainda funciona, pois a versão mais recente parece exigir que o valor retornado seja aReactElement
srph
Apenas envolva-o com outro elemento, <span>ou <div>.
tobik
Há um problema quando tentei usar isso para renderizar um <td> ou vazio em <table>, porque <noscript> não é aceitável como filho para <tr>. Caso contrário, funciona bem para mim.
Su verde
1
Se você estiver indo para fazer isso, não há um método transpilation que é muito bom: npmjs.com/package/jsx-control-statements
steve
82

Pessoalmente, acho que as expressões ternárias mostradas em ( JSX In Depth ) são a maneira mais natural que está de acordo com os padrões do ReactJs.

Veja o exemplo a seguir. É um pouco confuso à primeira vista, mas funciona muito bem.

<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
fonte
Nota: os colchetes são iguais a um retorno de componente; portanto, seu conteúdo precisa ser cercado por um <div> </div>.
JoeTidee 2/16/16
@Chiedo Por que você prefere a resposta popular? Isso parece funcionar muito bem.
Snowman
2
@moby Acho que a resposta popular é mais dinâmica e resulta em uma função de renderização mais limpa. Com essa abordagem, o que acontece se você tiver vários condicionais? Agora você terá uma loucura de aninhar com esse formato estranho e, se duas áreas separadas precisarem das mesmas alterações com base no condicional, você precisará reescrever o condicional novamente. Apenas minha opinião! :)
Chiedo 06/06
uma página relacionada também é bastante útil: facebook.github.io/react/docs/…
Neil
46

Você também pode escrever como

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

Se você state.banneré nullou undefined, o lado direito da condição é ignorado.

wialy
fonte
41

O Ifcomponente de estilo é perigoso porque o bloco de código é sempre executado independentemente da condição. Por exemplo, isso causaria uma exceção nula se banneré 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>
  );
}

Outra opção é usar uma função embutida (especialmente útil com instruções 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>
  );
}

Outra opção dos problemas de reação :

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
fonte
2
De todas as soluções até agora, a função embutida parece a mais elegante e direta. Algo que se deve tomar cuidado?
suryasankar
22

&& + estilo de código + componentes pequenos

Esta sintaxe de teste simples + convenção no estilo de código + pequenos componentes focados é para mim a opção mais legível disponível no mercado. Você só precisa ter um cuidado especial com valores falsos como false, 0ou "".

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

faça notação

A sintaxe da notação ES7 estágio 0 também é muito boa e eu definitivamente a utilizarei quando meu IDE oferecer suporte corretamente:

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

Mais detalhes aqui: ReactJs - Criando um componente "If" ... uma boa idéia?

Sebastien Lorber
fonte
Informações não-básica gratuita: o primeiro é chamadoshort-circuit syntax
Sospedra
1
@ Deerloper Acho que a diferença entre && e & é uma das primeiras coisas que aprendemos nas escolas de informática, mas algumas pessoas podem não saber, por isso não é uma má idéia dar mais explicações: en.wikipedia.org/wiki/Short-circuit_evaluation
Sebastien Lorber 29/03
É verdade, mas eles ensinam isso como parte das declarações condicionais clássicas. Eu encontrei um monte de gente confusa sobre como usar o curto-circuito para processar um Reagir componente;)
Sospedra
22

Simples, crie uma função.

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

Esse é um padrão que eu pessoalmente sigo o tempo todo. Torna o código realmente limpo e fácil de entender. Além disso, permite refatorar Bannerem seu próprio componente se ele for muito grande (ou reutilizado em outros lugares).

Michael Yagudaev
fonte
13

A dosintaxe experimental do ES7 facilita isso. Se você estiver usando o Babel, ative o es7.doExpressionsrecurso:

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

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

balexand
fonte
11

Como já mencionado nas respostas, o JSX apresenta duas opções

  • Operador ternário

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

  • Conjunção lógica

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


No entanto, esses não funcionam price == 0 .

O JSX renderizará a ramificação falsa no primeiro caso e, no caso de conjunção lógica, nada será renderizado. Se a propriedade puder ser 0, use apenas instruções if fora do seu JSX.

Lyubomir
fonte
Isso não é um problema JSX. É a própria condição, mesmo na JS simples se comportaria da mesma maneira. Se você quiser qualquer número, basta usar typeof(this.state.price) === "number"como condição (em qualquer uma das variantes).
Kroltan 4/10
8

Este componente funciona quando você tem mais de um elemento dentro da ramificação "if":

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

Uso:

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 alguém pensa que esses componentes não são bons para a leitura de código. Mas, na minha opinião, Html com javascript é pior

k.makarov
fonte
Como a resposta If aqui, isso ainda executará a condição falsa, mesmo que ela não seja exibida.
precisa saber é o seguinte
3

A maioria dos exemplos está com uma linha de "html" que é renderizada condicionalmente. Isso me parece legível quando tenho várias linhas que precisam ser renderizadas condicionalmente.

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

O primeiro exemplo é bom porque, em vez de null, podemos renderizar condicionalmente algum outro conteúdo, como{this.props.showContent ? content : otherContent}

Mas se você só precisa mostrar / ocultar conteúdo, isso é ainda melhor, pois Booleanos, Nulos e Indefinidos São Ignorados

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
fonte
2

Existe outra solução, se o componente 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
fonte
2

Eu uso um atalho mais explícito: Uma expressão de função chamada imediatamente (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
fonte
1

Criei https://www.npmjs.com/package/jsx-control-statements para facilitar um pouco, basicamente permite definir <If>condicionais como tags e depois compila-os em ifs ternários para que o código dentro do <If>único seja executado se a condição for verdadeira.

Alex Gilleran
fonte
1

Também há uma versão realmente limpa de uma linha ... {this.props.product.title || "Sem título"}

Ou seja:

render: function() {
            return (
                <div className="title">
                    { this.props.product.title || "No Title" }
                </div>
            );
        }
max kaplan
fonte
Isso funciona com a minha amostra acima? Onde a div do banner não deve aparecer se não houver suporte de banner?
Jack Allan
1

Recentemente, fiz https://github.com/ajwhite/render-if para renderizar elementos com segurança somente se o predicado for aprovado .

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

ou

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

//...

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

Você pode incluir condicionalmente elementos usando o operador ternário da seguinte forma:

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

Você pode usar uma função e retornar o componente e manter fina a função de renderização

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

Aqui está minha abordagem usando o 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'));

Demonstração: http://jsfiddle.net/kb3gN/16688/

Estou usando código como:

{opened && <SomeElement />}

Isso renderizará SomeElementapenas se openedfor verdadeiro. Funciona devido à maneira como o JavaScript resolve as condições lógicas:

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

Como Reactserá ignorado false, acho uma maneira muito boa de renderizar condicionalmente alguns elementos.

pie6k
fonte
1

Talvez ajude alguém que se depara com a pergunta: Todas as renderizações condicionais em React É um artigo sobre todas as diferentes opções de renderização condicional no React.

Principais informações sobre quando usar qual renderização condicional:

** se-mais

  • é a renderização condicional mais básica
  • iniciante amigável
  • use if para desativar antecipadamente um método de renderização retornando null

** operador ternário

  • use-o sobre uma declaração if-else
  • é mais conciso do que if-else

** operador lógico e&

  • use-o quando um lado da operação ternária retornar nulo

** caixa do interruptor

  • verboso
  • só pode ser embutido com a função de chamada automática
  • evite-o, use enums

** enumerações

  • perfeito para mapear estados diferentes
  • perfeito para mapear mais de uma condição

** renderizações condicionais de vários níveis / aninhadas

  • evitá-los por uma questão de legibilidade
  • divida componentes em componentes mais leves com sua própria renderização condicional simples
  • use HOCs

** HOCs

  • use-os para proteger a renderização condicional
  • componentes podem se concentrar em seu objetivo principal

** componentes externos de modelos

  • evite-os e fique à vontade com JSX e JavaScript
Robin Wieruch
fonte
1

Com o ES6, você pode fazer isso com uma única linha

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

"show" é um booleano e você usa essa classe

<If show={true}> Will show </If>
<If show={false}> WON'T show </div> </If>
Leon
fonte
1
Ruim. Sempre será calculado.
Qwertiy 17/05/19
0

Eu não acho que isso tenha sido mencionado. É como sua própria resposta, mas acho que é ainda mais simples. Você sempre pode retornar cadeias de caracteres das expressões e pode aninhar jsx dentro de expressões, portanto, isso permite uma expressão embutida fácil de ler.

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
fonte
0

Eu gosto da explicitação de Expressões de Função Imediatamente Invocadas ( IIFE) e if-elsemais render callbackse mais 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>
  );
}

Você só precisa se familiarizar com a IIFEsintaxe, {expression}é a sintaxe usual do React, por dentro, basta considerar que você está escrevendo uma função que está se chamando.

function() {

}()

que precisam ser embrulhados em parênteses

(function() {

}())
Gianluca Esposito
fonte
0

Há também uma técnica que usa objetos de renderização para renderizar condicionalmente um componente. Seu benefício é que a renderização não será avaliada até que a condição seja atendida, resultando em nenhuma preocupação com valores nulos e indefinidos .

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
fonte
0

Quando precisar renderizar algo apenas se a condição aprovada for preenchida, você poderá usar a sintaxe:

{ condition && what_to_render }

O código dessa maneira ficaria assim:

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

É claro que existem outras maneiras válidas de fazer isso, tudo depende das preferências e da ocasião. Você pode aprender mais maneiras de como renderizar condicional em React neste artigo, se estiver interessado!

Nesha Zoric
fonte
-1

Apenas para adicionar outra opção - se você gosta / tolera coffee-script, pode usar coffee-react para escrever seu JSX; nesse caso, as instruções if / else são utilizáveis, pois são expressões em coffee-script e não:

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