¿Cómo tener elementos condicionales y mantenerme SECO con JSX de Facebook React?


229

¿Cómo incluyo opcionalmente un elemento en JSX? Aquí hay un ejemplo usando un banner que debería estar en el componente si se ha pasado. Lo que quiero evitar es tener que duplicar las etiquetas HTML en la instrucción 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>
    );
}

3
¿Qué pasa si simplemente no tienes una elserama? ¿Funciona eso? No estoy familiarizado con jsx ...
Tom Fenech

1
Bien, esto ayudó. También vea github.com/facebook/react/issues/690
Gabriel Florit

lista exhaustiva de opciones para hacer renderizado condicional en React: robinwieruch.de/conditional-rendering-react
Robin Wieruch

Respuestas:


151

Simplemente deje el banner como indefinido y no se incluye.


1
No puedo pensar en nada más que basura al lado de esa respuesta ... como si me
hubiera

¿ nullFunciona tan bien como undefined? ¿También es esa parte de la especificación que funciona de esta manera y por lo tanto no es probable que se rompa en el futuro?
hippietrail

133

¿Qué hay de esto? Definamos un Ifcomponente de ayuda simple .

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

Y úsalo de esta manera:

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

ACTUALIZACIÓN: a medida que mi respuesta se está volviendo popular, me siento obligado a advertirle sobre el mayor peligro relacionado con esta solución. Como se señaló en otra respuesta, el código dentro del <If />componente se ejecuta siempre independientemente de si la condición es verdadera o falsa. Por lo tanto, el siguiente ejemplo fallará en caso de que bannersea null(tenga en cuenta el acceso a la propiedad en la segunda línea):

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

Tienes que tener cuidado cuando lo usas. Sugiero leer otras respuestas para enfoques alternativos (más seguros).

ACTUALIZACIÓN 2: Mirando hacia atrás, este enfoque no solo es peligroso sino también desesperadamente engorroso. Es un ejemplo típico de cuando un desarrollador (yo) intenta transferir patrones y enfoques que conoce de un área a otra, pero realmente no funciona (en este caso, otros lenguajes de plantilla).

Si necesita un elemento condicional, hágalo así:

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 también necesita la rama else, solo use un operador ternario:

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

Es mucho más corto, más elegante y seguro. Lo uso todo el tiempo. La única desventaja es que no se puede else iframificar tan fácilmente, pero eso no suele ser tan común.

De todos modos, esto es posible gracias a cómo funcionan los operadores lógicos en JavaScript. Los operadores lógicos incluso permiten pequeños trucos como este:

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

1
Gracias. Recomiendo leer este github.com/facebook/react/issues/690 Este enlace ha sido publicado en los comentarios debajo de esta pregunta y lo noté después de haber publicado mi solución. Si lo verifica, algún chico también menciona esta solución, pero no la recomienda. Personalmente, creo que está bien, al menos en el caso del OP, pero es cierto que de esta manera no es perfecto (no hay una buena manera de definir otra rama, por ejemplo). De todos modos, vale la pena leerlo.
tobik

1
No estoy seguro de si esto todavía funciona ya que la última versión parece requerir que el valor devuelto sea unReactElement
srph

Simplemente envuélvelo con otro elemento, <span>o <div>.
tobik

Hay un problema cuando traté de usar esto para representar un <td> o vacío en <table>, porque <noscript> no es aceptable como elemento secundario para <tr>. De lo contrario, funciona bien para mí.
Green Su

1
Si va a hacer esto, hay un método de transpilación que es bastante bueno: npmjs.com/package/jsx-control-statements
steve

82

Personalmente, realmente creo que las expresiones ternarias que se muestran en ( JSX In Depth ) son la forma más natural que se ajusta a los estándares ReactJs.

Ver el siguiente ejemplo. Es un poco desordenado a primera vista, pero funciona bastante 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>

Nota: los corchetes son lo mismo que un retorno de componente, por lo que su contenido debe estar rodeado por un <div> </div>.
JoeTidee

@Chiedo, ¿por qué prefieres la respuesta popular? Esto parece funcionar muy bien.
Muñeco de nieve

2
@moby Creo que la respuesta popular es más dinámica y da como resultado una función de renderizado más limpia. Con este enfoque, ¿qué sucede si tiene múltiples condicionales? Ahora tendrá una locura de anidar con este formato extraño y si dos áreas separadas necesitan los mismos cambios en función del condicional, ahora debe volver a escribir el condicional nuevamente. ¡Solo mi opinión! :)
Chiedo

una página relacionada también es bastante útil: facebook.github.io/react/docs/…
Neil

46

También puedes escribirlo como

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

Si state.banneres nullo undefined, se omite el lado derecho de la condición.


41

El Ifcomponente de estilo es peligroso porque el bloque de código siempre se ejecuta independientemente de la condición. Por ejemplo, esto causaría una excepción nula si banneres 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>
  );
}

Otra opción es usar una función en línea (especialmente útil con sentencias 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>
  );
}

Otra opción de los problemas de reacción :

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

2
De todas las soluciones hasta ahora, la función en línea parece la más elegante y directa. ¿Algo que uno debe tener cuidado?
suryasankar

22

&& + código-estilo + componentes pequeños

Esta simple sintaxis de prueba + convención de estilo de código + pequeños componentes enfocados es para mí la opción más legible que existe. Solo necesita tener especial cuidado con los valores falsos como false, 0o "".

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

hacer notación

La sintaxis de notación do7 stage-0 de ES7 también es muy buena y definitivamente la usaré cuando mi IDE lo admita correctamente:

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

Más detalles aquí: ReactJs - Crear un componente "If" ... ¿una buena idea?


Información no básica gratuita: la primera se llamashort-circuit syntax
sospedra

1
@Deerloper Creo que la diferencia entre && y & es una de las primeras cosas que aprendemos en las escuelas de informática, pero algunas personas pueden no saberlo, por lo que no es una mala idea dar más explicaciones: en.wikipedia.org/wiki/Short-circuit_evaluation
Sebastien Lorber el

Es cierto, pero lo enseñan como parte de las declaraciones condicionales clásicas. He encontrado a mucha gente confundida sobre el uso del cortocircuito para representar un componente React;)
sospedra

22

Simple, crea una función.

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

Este es un patrón que yo personalmente sigo todo el tiempo. Hace que el código sea realmente limpio y fácil de entender. Además, le permite refactorizar Banneren su propio componente si se vuelve demasiado grande (o se reutiliza en otros lugares).



11

Como ya se mencionó en las respuestas, JSX le presenta dos opciones

  • Operador ternario

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

  • Conjunción lógica

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


Sin embargo, esos no funcionan price == 0 .

JSX representará la rama falsa en el primer caso y en caso de conjunción lógica, no se representará nada. Si la propiedad puede ser 0, solo use sentencias if fuera de su JSX.


Eso no es un problema de JSX. Es la condición en sí misma, incluso en JS se comportaría igual. Si desea cualquier número, simplemente use typeof(this.state.price) === "number"como condición (en cualquiera de las variantes).
Kroltan

8

Este componente funciona cuando tiene más de un elemento dentro de la rama "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 alguien piensa que estos componentes no son buenos para la lectura de código. Pero en mi opinión, HTML con JavaScript es peor


Al igual que la respuesta If aquí, esto seguirá ejecutando la condición falsa aunque no se muestre.
Keegan 82

3

La mayoría de los ejemplos son con una línea de "html" que se representa condicionalmente. Esto parece legible para mí cuando tengo varias líneas que deben representarse 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>
  );
}

El primer ejemplo es bueno porque en lugar de nullque podamos renderizar condicionalmente algún otro contenido como{this.props.showContent ? content : otherContent}

Pero si solo necesita mostrar / ocultar contenido, esto es aún mejor ya que se ignoran los valores booleanos, nulos e indefinidos

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

2

Hay otra solución, si componente para 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

Utilizo un acceso directo más explícito: una expresión de función invocada inmediatamente (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})}/>
  }
})()}


1

También hay una versión realmente limpia de una línea ... {this.props.product.title || "Sin título" }

Es decir:

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

¿Funciona con mi muestra anterior? ¿Dónde el banner div no debería aparecer si no hay un accesorio de banner?
Jack Allan

1

Hice https://github.com/ajwhite/render-if recientemente para representar elementos de forma segura solo si el predicado pasa .

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

o

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

//...

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

1

Puede incluir elementos condicionalmente utilizando el operador ternario de esta manera:

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

Puede usar una función y devolver el componente y mantener delgada la función de renderizado

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

Aquí está mi enfoque usando 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'));

Demostración: http://jsfiddle.net/kb3gN/16688/

Estoy usando un código como:

{opened && <SomeElement />}

Eso rendirá SomeElementsolo si openedes cierto. Funciona debido a la forma en que JavaScript resuelve las condiciones 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 Reactignoraré false, considero que es una muy buena forma de renderizar condicionalmente algunos elementos.


1

Tal vez ayude a alguien que se encuentre con la pregunta: todas las representaciones condicionales en React Es un artículo sobre todas las diferentes opciones para la representación condicional en React.

Conclusiones clave sobre cuándo usar qué representación condicional:

** if-else

  • es la representación condicional más básica
  • principiante amigable
  • use if para darse de baja anticipadamente de un método de renderizado devolviendo nulo

** operador ternario

  • úselo sobre una declaración if-else
  • es más conciso que si no

** operador lógico &&

  • úselo cuando un lado de la operación ternaria devuelva nulo

** caja de interruptor

  • verboso
  • solo se puede alinear con la función de invocación automática
  • evítalo, usa enumeraciones en su lugar

** enumeraciones

  • perfecto para mapear diferentes estados
  • perfecto para mapear más de una condición

** representaciones condicionales anidadas / multinivel

  • evitarlos por el bien de la legibilidad
  • dividir componentes en componentes más livianos con su propia representación condicional simple
  • usar HOC

** HOC

  • úsalos para proteger la representación condicional
  • los componentes pueden enfocarse en su propósito principal

** componentes de plantillas externas

  • evítalos y siéntete cómodo con JSX y JavaScript

1

Con ES6 puedes hacerlo con una simple línea

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

"show" es un booleano y usas esta clase por

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

1
Malo. Siempre se calculará.
Qwertiy

0

No creo que esto haya sido mencionado. Esta es como su propia respuesta, pero creo que es aún más simple. Siempre puede devolver cadenas de las expresiones y puede anidar jsx dentro de las expresiones, por lo que esto permite una expresión en línea fácil de leer.

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

Me gusta lo explícito de las expresiones de función invocadas inmediatamente ( IIFE) y if-elseover render callbacksand 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>
  );
}

Solo necesita familiarizarse con la IIFEsintaxis, {expression}es la sintaxis React habitual, en su interior solo considere que está escribiendo una función que se invoca a sí misma.

function() {

}()

que necesitan ser envueltos dentro de parens

(function() {

}())

0

También hay una técnica que utiliza accesorios de renderizado para renderizar condicionalmente un componente. Su beneficio es que el render no se evaluaría hasta que se cumpla la condición, lo que no genera preocupaciones por 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>
        )}
      />
    )
  }
}

0

Cuando tenga que representar solo algo si se cumple la condición aprobada, puede usar la sintaxis:

{ condition && what_to_render }

El código de esta manera se vería así:

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

Por supuesto, hay otras formas válidas de hacer esto, todo depende de las preferencias y la ocasión. ¡Puedes aprender más formas sobre cómo hacer renderizado condicional en React en este artículo si estás interesado!


-1

Solo para agregar otra opción: si le gusta / tolera coffee-script, puede usar coffee-react para escribir su JSX, en cuyo caso las declaraciones if / else son utilizables, ya que son expresiones en coffee-script y no declaraciones:

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.