¿Cómo agrego condicionalmente atributos a los componentes React?

551

¿Hay alguna manera de agregar solo atributos a un componente Reaccionar si se cumple una determinada condición?

Se supone que debo agregar los atributos obligatorios y readOnly para formar elementos basados ​​en una llamada Ajax después del renderizado, pero no puedo ver cómo resolver esto ya que readOnly = "false" no es lo mismo que omitir el atributo por completo.

El siguiente ejemplo debería explicar lo que quiero, pero no funcionará (Error de análisis: identificador inesperado).

var React = require('React');

var MyOwnInput = React.createClass({
    render: function () {
        return (
            <div>
                <input type="text" onChange={this.changeValue} value={this.getValue()} name={this.props.name}/>
            </div>
        );
    }
});

module.exports = React.createClass({
    getInitialState: function () {
        return {
            isRequired: false
        }
    },
    componentDidMount: function () {
        this.setState({
            isRequired: true
        });
    },
    render: function () {
        var isRequired = this.state.isRequired;

        return (
            <MyOwnInput name="test" {isRequired ? "required" : ""} />
        );
    }
});
Remi Sture
fuente
1
Puede ser un comentario que ayude a alguien, descubrí que React 16.7 no se retracta y actualiza los atributos html del componente si solo los cambiaste en una tienda (fe redux) y lo vinculaste al componente. Esto significa que el componente tiene fe aria-modal=true, empuja los cambios (a falso) al almacén de atributos aria / datos , pero no se cambia nada más (como el contenido del componente o la clase o las variables allí) como resultado, ReactJs no actualizará aria / atributos de datos en esos componentes. He estado jugando todo el día para darme cuenta de eso.
AlexNikonov

Respuestas:

535

Aparentemente, para ciertos atributos, React es lo suficientemente inteligente como para omitir el atributo si el valor que le pasas no es verdadero. Por ejemplo:

var InputComponent = React.createClass({
    render: function() {
        var required = true;
        var disabled = false;

        return (
            <input type="text" disabled={disabled} required={required} />
        );
    }
});

resultará en:

<input type="text" required>

Actualización: si alguien tiene curiosidad sobre cómo / por qué sucede esto, puede encontrar detalles en el código fuente de ReactDOM, específicamente en las líneas 30 y 167 del archivo DOMProperty.js .

juandemarco
fuente
45
Generalmente nullsignifica "actuar como si no lo hubiera especificado en absoluto". Para los atributos dom booleanos verdadero / falso es preferible a repetir el nombre del atributo / falso, por ejemplo, <input disabled>compila aReact.createElement('input', {disabled: true})
Brigand
Estoy de acuerdo. Repito el nombre porque tuve problemas en el pasado con ciertos navegadores y el hábito me atrapó tanto que agregué manualmente la ="required"parte. Chrome en realidad renderizó solo el atributo sin el valor.
juandemarco 01 de
8
readonlynunca se agrega porque React está esperando el atributo readOnly(con una O mayúscula).
Max
8
¡Gracias! Asegúrese de que el valor no sea solo una cadena vacía o cero, es posible que no se eliminen. Por ejemplo, se puede pasar un valor de este tipo, y debe asegurarse de que se elimina si se evalúa como falsa: alt={props.alt || null}.
Jake
55
Gracias, @Jake. Intenté establecer el atributo en false, pero solo me nullaseguré de que el atributo se haya eliminado realmente.
Nathan Arthur
387

La respuesta de juandemarco suele ser correcta, pero aquí hay otra opción.

Construye un objeto como quieras:

var inputProps = {
  value: 'foo',
  onChange: this.handleChange
};

if (condition)
  inputProps.disabled = true;

Render con extensión, opcionalmente pasando otros accesorios también.

<input
    value="this is overridden by inputProps"
    {...inputProps}
    onChange={overridesInputProps}
 />
Bandido
fuente
14
Esto es realmente muy útil, especialmente al agregar muchas propiedades condicionalmente (y yo personalmente no tenía idea de que podría hacerse de esta manera).
juandemarco 01 de
1
Muy elegante, pero ¿no debería ser inputProps.disabled = true?
Joppe
Muy simple, he hecho que mi código sea más legible sin tener múltiples condiciones.
Naveen Kumar PG
Si a alguien le importa la semántica precisa de este "azúcar", puede ver el script en el que se transpuso su .jsx y verá que _extendsse le ha agregado una función , que (en circunstancias normales) tomará el propsconstruido de los "atributos normales" y se aplican Object.assign(props, inputProps).
Nathan Chappell
299

Aquí es un ejemplo del uso Bootstrap 's Buttona través de Reaccionar-Bootstrap (versión 0.32.4):

var condition = true;

return (
  <Button {...(condition ? {bsStyle: 'success'} : {})} />
);

Dependiendo de la condición, {bsStyle: 'success'}o {}será devuelto. El operador de propagación distribuirá las propiedades del objeto devuelto al Buttoncomponente. En el caso falso, dado que no existen propiedades en el objeto devuelto, no se pasará nada al componente.


Una forma alternativa basada en el comentario de Andy Polhill :

var condition = true;

return (
  <Button bsStyle={condition ? 'success' : undefined} />
);

La única pequeña diferencia es que en el segundo ejemplo que el componente interno <Button/>'s propsobjeto tendrá una clave bsStylecon un valor de undefined.

Arman Yeghiazaryan
fuente
55
@Punit, el operador de propagación tiene una precedencia más baja que el operador condicional, por lo que la condición se evalúa primero ( {bsStyle: 'success'}o lo que {}resulta de ella), luego ese objeto se propaga.
Victor Zamanian
55
¿Lo siguiente haría lo mismo? <Button bsStyle={ condition ? 'success': undefined } /> Creo que la sintaxis es un poco más fácil, pasar undefinedomitirá la propiedad.
Andy Polhill
3
@AndyPolhill se ve bien para mí y es mucho más fácil de leer el código, la única pequeña diferencia es que en el ejemplo del código <Button/>, el propsobjeto del componente interno tendrá una clave bsStylecon valor de undefined.
Arman Yeghiazaryan
@AndyPolhill, la razón por la que la sintaxis parece más difícil de leer es porque le faltan algunos paréntesis implícitos, lo que hace que el ejemplo parezca extraño y difícil de entender. Edición del ejemplo para agregar paréntesis.
Govind Rai
1
El primer método funcionó perfectamente para mí, gracias
Liran H
86

Aquí hay una alternativa.

var condition = true;

var props = {
  value: 'foo',
  ...( condition && { disabled: true } )
};

var component = <div { ...props } />;

O su versión en línea

var condition = true;

var component = (
  <div
    value="foo"
    { ...( condition && { disabled: true } ) } />
);
Temporada
fuente
99
Me gusta este enfoque, me hace genial entre mis compañeros de trabajo. Bromas aparte, por lo que parece, los accesorios solo se pasan como un par clave-valor después de todo, ¿es correcto?
JohnnyQ
1
Si conditiones falso, esto intentará expandirse / iterar sobre falso, lo que no creo que sea correcto.
Lars Nyström
1
@ LarsNyström, eso tiene sentido. El operador de propagación solo acepta iterables, donde falseno es uno. Solo verifique con Babel. Esto funciona cuando se conditionevalúa como falso ya que Babel implementa el operador. Aunque podría ser una solución trivial ...( condition ? { disabled: true } : {} ), entonces se vuelve un poco detallado. Gracias por esta buena entrada!
Temporada
+1 Este enfoque es necesario si desea generar condicionalmente atributos data-*o aria-*atributos, ya que son un caso especial en JSX, por data-my-conditional-attr={ false }lo tanto, generará en data-my-conditional-attr="false"lugar de omitir el atributo. facebook.github.io/react/docs/dom-elements.html
ptim
32

Aquí hay una forma en que lo hago.

Con un condicional :

<Label
    {...{
      text: label,
      type,
      ...(tooltip && { tooltip }),
      isRequired: required
    }}
/>

Todavía prefiero usar la forma regular de pasar los accesorios, porque es más legible (en mi opinión) en el caso de que no tenga ningún tipo de condicionales.

Sin un condicional :

<Label text={label} type={type} tooltip={tooltip} isRequired={required} />
Tony Tai Nguyen
fuente
¿Podría explicar cómo funciona esta parte ...(tooltip && { tooltip }),? Funciona en el componente, pero cuando trato de usar algo como esto en el código recibo un error que significa que trato de difundir el valor no iterable
skwisgaar
probablemente porque falseyValue && {}devolverá falso, por lo que es probable que se esté extendiendo en falso, por ejemplo ...(false). mucho mejor usar la expresión completa para que la propagación continúe comportándose ...(condition ? {foo: 'bar'} : {})
lfender6445
19

Puede usar el mismo acceso directo, que se usa para agregar / eliminar (partes de) componentes ( {isVisible && <SomeComponent />}).

class MyComponent extends React.Component {
  render() {
    return (
      <div someAttribute={someCondition && someValue} />
    );
  }
}
GijsjanB
fuente
3
Si someConditiones verdadero pero someValuees falso (por ejemplo false, sí mismo 0, etc.), ¿el atributo todavía se incluye? Esto es importante si es necesario incluir explícitamente un valor falso, por ejemplo, a 0para un atributo de coordenadas, etc.
Andrew Willems
Normalmente, el atributo se omite, pero no en el caso de data-*y aria-*, vea mi comentario más arriba. Si cita el valor o lo someAttr={ `${falsyValue}` }convierte en una Cadena, el atributo mostrará: por ejemplo, podría renderizarsomeAttr="false"
ptim
19

Digamos que queremos agregar una propiedad personalizada (usando aria- * o data- *) si una condición es verdadera:

{...this.props.isTrue && {'aria-name' : 'something here'}}

Digamos que queremos agregar una propiedad de estilo si una condición es verdadera:

{...this.props.isTrue && {style : {color: 'red'}}}
Mina Luke
fuente
10

Si usa ECMAScript 6, simplemente puede escribir así.

// First, create a wrap object.
const wrap = {
    [variableName]: true
}
// Then, use it
<SomeComponent {...{wrap}} />
snyh
fuente
6

Esto debería funcionar, ya que su estado cambiará después de la llamada Ajax, y el componente padre se volverá a procesar.

render : function () {
    var item;
    if (this.state.isRequired) {
        item = <MyOwnInput attribute={'whatever'} />
    } else {
        item = <MyOwnInput />
    }
    return (
        <div>
            {item}
        </div>
    );
}
Michael Parker
fuente
3

En React puede renderizar condicionalmente Componentes, pero también sus atributos, como accesorios, className, id y más.

En React es una muy buena práctica usar el operador ternario que puede ayudarlo a procesar componentes condicionalmente.

Un ejemplo también muestra cómo renderizar condicionalmente Component y su atributo de estilo.

Aquí hay un ejemplo simple:

class App extends React.Component {
  state = {
    isTrue: true
  };

  render() {
    return (
      <div>
        {this.state.isTrue ? (
          <button style={{ color: this.state.isTrue ? "red" : "blue" }}>
            I am rendered if TRUE
          </button>
        ) : (
          <button>I am rendered if FALSE</button>
        )}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>

Juraj
fuente
Esto puede ser muy complicado con muchos atributos. Me gusta más la variante de propagación.
Remi Sture
Sí, tiene razón, pero esto es para alguien que necesita obtener una visión general. Haré otro ejemplo. Pero quiero mantener las cosas simples.
Juraj
0

Teniendo en cuenta la publicación JSX In Depth , puede resolver su problema de esta manera:

if (isRequired) {
  return (
    <MyOwnInput name="test" required='required' />
  );
}
return (
    <MyOwnInput name="test" />
);
Viktor Kireev
fuente