Comment ajouter conditionnellement des attributs aux composants React?

551

Existe-t-il un moyen d'ajouter des attributs à un composant React uniquement si une certaine condition est remplie?

Je suis censé ajouter des attributs requis et readOnly pour former des éléments basés sur un appel Ajax après le rendu, mais je ne vois pas comment résoudre ce problème car readOnly = "false" n'est pas la même chose que d'omettre complètement l'attribut.

L'exemple ci-dessous devrait expliquer ce que je veux, mais cela ne fonctionnera pas (erreur d'analyse: identifiant inattendu).

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
la source
1
Peut-être qu'un commentaire aide quelqu'un, j'ai découvert que React 16.7 ne restitue pas et met à jour les attributs html du composant si vous ne les avez modifiés que dans un magasin (fe redux) et liés au composant. Cela signifie que le composant a fe aria-modal=true, vous poussez les modifications (sur false) dans le magasin d' attributs aria / data , mais rien d'autre n'est modifié (comme le contenu ou la classe du composant ou les variables dedans) car le résultat ReactJs ne mettra pas à jour aria / attrs de données dans ces composants. J'ai déconné toute la journée pour le réaliser.
AlexNikonov

Réponses:

535

Apparemment, pour certains attributs, React est suffisamment intelligent pour omettre l'attribut si la valeur que vous lui passez n'est pas véridique. Par exemple:

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

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

aura pour résultat:

<input type="text" required>

Mise à jour: si quelqu'un est curieux de savoir comment / pourquoi cela se produit, vous pouvez trouver des détails dans le code source de ReactDOM, en particulier aux lignes 30 et 167 du fichier DOMProperty.js .

juandemarco
la source
45
Signifie généralement null"agir comme si je ne l'avais pas spécifié du tout". Pour les attributs booléens dom, true / false est préférable à la répétition du nom d'attribut / false, par exemple, <input disabled>compile àReact.createElement('input', {disabled: true})
Brigand
Je suis d'accord. Je répète le nom car j'ai eu des problèmes dans le passé avec certains navigateurs et l'habitude m'a tellement retenu que j'ai ajouté manuellement la ="required"pièce. Chrome n'a rendu que l'attribut sans la valeur.
juandemarco
8
readonlyn'est jamais ajouté car React attend l'attribut readOnly(avec un O majuscule).
Max
8
Merci! Assurez-vous que la valeur n'est pas seulement une chaîne vide ou zéro, ceux-ci peuvent ne pas être supprimés. Par exemple, vous pouvez passer une valeur comme celui - ci, et il devrait vous assurer qu'il est supprimée si elle est fausse: alt={props.alt || null}.
Jake
5
Merci, @Jake. J'avais essayé de définir l'attribut sur false, mais je m'étais seulement nullassuré que l'attribut était réellement supprimé.
Nathan Arthur
387

réponse de juandemarco est généralement correcte, mais voici une autre option.

Construisez un objet comme vous le souhaitez:

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

if (condition)
  inputProps.disabled = true;

Rendu avec propagation, en passant éventuellement d'autres accessoires également.

<input
    value="this is overridden by inputProps"
    {...inputProps}
    onChange={overridesInputProps}
 />
Brigand
la source
14
C'est en fait très utile, en particulier lors de l'ajout conditionnel de nombreuses propriétés (et je n'avais personnellement aucune idée que cela pourrait être fait de cette façon).
juandemarco
1
Très élégant, mais ne devrait-il pas être inputProps.disabled = true?
Joppe
très simple, j'ai rendu mon code plus lisible sans avoir plusieurs conditions.
Naveen Kumar PG
Si quelqu'un se soucie de la sémantique précise de ce "sucre", vous pouvez regarder le script dans lequel votre .jsx est transposé, vous verrez qu'une fonction y _extendsa été ajoutée, qui (dans des circonstances normales) prendra la propsconstruction de les "attributs normaux" et appliquer Object.assign(props, inputProps).
Nathan Chappell
299

Voici un exemple d'utilisation Bootstrap « s Buttonvia React-Bootstrap (version 0.32.4):

var condition = true;

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

Selon l'état, soit {bsStyle: 'success'}ou {}sera retourné. L'opérateur d'étalement répartit ensuite les propriétés de l'objet renvoyé vers le Buttoncomposant. Dans le cas falsifié, car aucune propriété n'existe sur l'objet retourné, rien ne sera transmis au composant.


Une manière alternative basée sur le commentaire d'Andy Polhill :

var condition = true;

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

La seule petite différence est que , dans le deuxième exemple , le composant intérieur <Button/>de » propsl'objet aura une clé bsStyleavec une valeur de undefined.

Arman Yeghiazaryan
la source
5
@Punit, L'opérateur d'étalement a une priorité plus faible que l'opérateur conditionnel, donc la condition est évaluée en premier, ( {bsStyle: 'success'}ou en {}résulte), puis cet objet est étalé.
Victor Zamanian
5
Est-ce que ce qui suit ferait la même chose, <Button bsStyle={ condition ? 'success': undefined } /> je trouve la syntaxe un peu plus facile, le passage undefinedomettra la propriété.
Andy Polhill
3
@AndyPolhill me semble bien et beaucoup plus facile à lire le code, la seule petite différence est que dans votre exemple de code, <Button/>l' propsobjet du composant interne aura une clé bsStyleavec une valeur de undefined.
Arman Yeghiazaryan
@AndyPolhill la raison pour laquelle la syntaxe semble plus difficile à lire est qu'il manque des parenthèses implicites, ce qui donne à l'exemple un aspect étranger et plus difficile à comprendre. Modification de l'exemple pour ajouter des parenthèses.
Govind Rai
1
La première méthode a parfaitement fonctionné pour moi, merci
Liran H
86

Voici une alternative.

var condition = true;

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

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

Ou sa version en ligne

var condition = true;

var component = (
  <div
    value="foo"
    { ...( condition && { disabled: true } ) } />
);
Saison
la source
9
J'aime cette approche, ça me rend cool parmi mes collègues. Blague à part, à première vue, les accessoires sont juste passés comme une paire clé-valeur après tout, est-ce correct?
JohnnyQ
1
Si conditionest faux, cela tentera de développer / itérer sur faux, ce qui ne me semble pas correct.
Lars Nyström
1
@ LarsNyström, Cela a du sens. L'opérateur d'étalement accepte uniquement les itérables, où il falsen'en est pas un. Vérifiez auprès de Babel. Cela fonctionne avec lorsqu'il est conditionévalué comme faux depuis la façon dont Babel implémente l'opérateur. Bien qu'une solution de contournement triviale puisse être ...( condition ? { disabled: true } : {} ), elle devient alors un peu verbeuse. Merci pour cette belle entrée!
Saison
+1 Cette approche est requise si vous souhaitez émettre data-*ou aria-*attribuer des attributs de manière conditionnelle , car ils sont un cas spécial dans JSX, de sorte data-my-conditional-attr={ false }que la sortie sera data-my-conditional-attr="false"plutôt que d'omettre l'attribut. facebook.github.io/react/docs/dom-elements.html
ptim
32

Voici une façon de procéder.

Avec un conditionnel :

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

Je préfère toujours utiliser la méthode habituelle de transmission des accessoires, car elle est plus lisible (à mon avis) dans le cas où il n'y a pas de conditionnel.

Sans conditionnel :

<Label text={label} type={type} tooltip={tooltip} isRequired={required} />
Tony Tai Nguyen
la source
Pourriez-vous pls expliquer comment cette partie fonctionne - ...(tooltip && { tooltip }),? Cela fonctionne sur le composant, mais lorsque j'essaie d'utiliser quelque chose comme ça dans le code, j'obtiens une erreur signifiant que j'essaie de diffuser une valeur non itérable
skwisgaar
probablement parce falseyValue && {}que retournera faux, donc il est probable que vous étaliez sur faux, par exemple ...(false). il vaut mieux utiliser la pleine expression pour que la propagation continue de se comporter ...(condition ? {foo: 'bar'} : {})
lfender6445
19

Vous pouvez utiliser le même raccourci, qui est utilisé pour ajouter / supprimer (des parties de) composants ( {isVisible && <SomeComponent />}).

class MyComponent extends React.Component {
  render() {
    return (
      <div someAttribute={someCondition && someValue} />
    );
  }
}
GijsjanB
la source
3
Si someConditionest vrai mais someValueest faux (par exemple falselui-même, ou 0, etc.), l'attribut est-il toujours inclus? Ceci est important s'il est nécessaire d'inclure explicitement une valeur falsifiée, par exemple un 0pour un attribut de coordonnées, etc.
Andrew Willems
Normalement, l'attribut est omis, mais pas dans le cas de data-*et aria-*, voir mon commentaire ci-dessus. Si vous citez la valeur ou la someAttr={ `${falsyValue}` }someAttr="false"
transformez en
19

Disons que nous voulons ajouter une propriété personnalisée (en utilisant aria- * ou data- *) si une condition est vraie:

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

Disons que nous voulons ajouter une propriété de style si une condition est vraie:

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

Si vous utilisez ECMAScript 6, vous pouvez simplement écrire comme ceci.

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

Cela devrait fonctionner, car votre état changera après l'appel Ajax et le composant parent sera rendu de nouveau.

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

Dans React, vous pouvez effectuer un rendu conditionnel des composants, mais également de leurs attributs, tels que les accessoires, className, id, etc.

Dans React, il est très recommandé d'utiliser l' opérateur ternaire qui peut vous aider à effectuer le rendu conditionnel des composants.

Un exemple montre également comment restituer conditionnellement Component et son attribut de style.

Voici un exemple 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
la source
Cela peut devenir très compliqué avec beaucoup d'attributs. J'aime mieux la variante spread.
Remi Sture
Oui vous avez raison mais c'est pour quelqu'un qui a besoin d'avoir un aperçu je vais faire un autre exemple. Mais voulez garder les choses simples.
Juraj
0

En considérant le post JSX In Depth , vous pouvez résoudre votre problème de cette façon:

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