web-dev-qa-db-fra.com

n'autorise que les enfants d'un type spécifique dans un composant de réaction

J'ai un composant Card et un composant CardGroup et j'aimerais émettre une erreur lorsque CardGroup a des enfants qui ne sont pas des composants Card. Est-ce possible ou est-ce que j'essaie de résoudre le mauvais problème?

27
bigblind

Vous pouvez utiliser le nom d'affichage pour chaque enfant, accessible via le type:

for (child in this.props.children){
  if (this.props.children[child].type.displayName != 'Card'){
    console.log("Warning CardGroup has children that aren't Card components");
  }  
}
14
Mark

Pour React 0.14+ et en utilisant les classes ES6, la solution ressemblera à ceci:

class CardGroup extends Component {
  render() {
    return (
      <div>{this.props.children}</div>
    )
  }
}
CardGroup.propTypes = {
  children: function (props, propName, componentName) {
    const prop = props[propName]

    let error = null
    React.Children.forEach(prop, function (child) {
      if (child.type !== Card) {
        error = new Error('`' + componentName + '` children should be of type `Card`.');
      }
    })
    return error
  }
}
39
Diego V

Vous pouvez utiliser une fonction personnalisée propType pour valider les enfants, car ceux-ci ne sont que des accessoires. J'ai aussi écrit un article sur ceci, si vous voulez plus de détails.

var CardGroup = React.createClass({
  propTypes: {
    children: function (props, propName, componentName) {
      var error;
      var prop = props[propName];

      React.Children.forEach(prop, function (child) {
        if (child.type.displayName !== 'Card') {
          error = new Error(
            '`' + componentName + '` only accepts children of type `Card`.'
          );
        }
      });

      return error;
    }
  },

  render: function () {
    return (
      <div>{this.props.children}</div>
    );
  }
});
13
mzabriskie
static propTypes = {

  children : (props, propName, componentName) => {
              const prop = props[propName];
              return React.Children
                       .toArray(prop)
                       .find(child => child.type !== Card) && new Error(`${componentName} only accepts "<Card />" elements`);
  },

}
4
Abdennour TOUMI

J'ai créé un type PropType personnalisé que j'appelle equalTo. Vous pouvez l'utiliser comme ça ...

class MyChildComponent extends React.Component { ... }

class MyParentComponent extends React.Component {
  static propTypes = {
    children: PropTypes.arrayOf(PropTypes.equalTo(MyChildComponent))
  }
}

Maintenant, MyParentComponent accepte uniquement les enfants qui sont MyChildComponent. Vous pouvez vérifier des éléments HTML comme celui-ci ...

PropTypes.equalTo('h1')
PropTypes.equalTo('div')
PropTypes.equalTo('img')
...

Voici la mise en place ...

React.PropTypes.equalTo = function (component) {
  return function validate(propValue, key, componentName, location, propFullName) {
    const prop = propValue[key]
    if (prop.type !== component) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  };
}

Vous pouvez facilement l'étendre pour accepter l'un des nombreux types possibles. Peut-être que quelque chose comme ...

React.PropTypes.equalToOneOf = function (arrayOfAcceptedComponents) {
...
}
3
Charlie Martin

Vous pouvez ajouter un accessoire à votre composant Card, puis rechercher cet accessoire dans votre composant CardGroup. C’est le moyen le plus sûr d’y parvenir dans React.

Cet accessoire peut être ajouté en tant que defaultProp afin qu'il soit toujours là.

class Card extends Component {

  static defaultProps = {
    isCard: true,
  }

  render() {
    return (
      <div>A Card</div>
    )
  }
}

class CardGroup extends Component {

  render() {
    for (child in this.props.children) {
      if (!this.props.children[child].props.isCard){
        console.error("Warning CardGroup has a child which isn't a Card component");
      }
    }

    return (
      <div>{this.props.children}</div>
    )
  }
}

Vérifier si le composant Carte est bien un composant de Carte à l'aide de type ou displayName n'est pas sans danger car il risque de ne pas fonctionner pendant la production, comme indiqué ici: https://github.com/facebook/react/issues/6167#issuecomment -191243709

2
Hedley Smith

Pour ceux qui, comme moi, utilisent la version TypeScript . Vous pouvez filtrer/modifier les composants de la manière suivante:

this.modifiedChildren = React.Children.map(children, child => {
            if (React.isValidElement(child) && (child as React.ReactElement<any>).type === Card) {
                let modifiedChild = child as React.ReactElement<any>;
                // Modifying here
                return modifiedChild;
            }
            // Returning other components / string.
            // Delete next line in case you dont need them.
            return child;
        });
2
SLCH000

Pour valider le composant enfants approprié, combinez l'utilisation de react children foreach et des types de validation Custom , afin que vous disposiez des éléments suivants:

HouseComponent.propTypes = {
children: PropTypes.oneOfType([(props, propName, componentName) => {
    let error = null;
    const validInputs = [
    'Mother',
    'Girlfried',
    'Friends',
    'Dogs'
    ];
    // Validate the valid inputs components allowed.
    React.Children.forEach(props[propName], (child) => {
            if (!validInputs.includes(child.type.name)) {
                error = new Error(componentName.concat(
                ' children should be one of the type:'
                    .concat(validInputs.toString())
            ));
        }
    });
    return error;
    }]).isRequired
};

Comme vous pouvez le voir, avoir et tableau avec le nom du type correct.

D'autre part, il existe également une fonction appelée composantWithName de la bibliothèque airbnb/prop-types qui permet d'obtenir le même résultat . Vous pouvez voir plus de détails ici

HouseComponent.propTypes = {
    children: PropTypes.oneOfType([
        componentWithName('SegmentedControl'),
        componentWithName('FormText'),
        componentWithName('FormTextarea'),
        componentWithName('FormSelect')
    ]).isRequired
};

J'espère que cela aidera quelqu'un :)

1
Ismael Terreno

Utilisez la méthode React.Children.forEach pour parcourir les enfants et utilisez la propriété name pour vérifier le type:

React.Children.forEach(this.props.children, (child) => {
    if (child.type.name !== Card.name) {
        console.error("Only card components allowed as children.");
    }
}

Je recommande d'utiliser Card.name au lieu de 'Card' chaîne pour un meilleur maintien et une meilleure stabilité vis-à-vis de uglify.

Voir: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name

1
Salim

J'ai publié le package permettant de valider les types d'éléments React https://www.npmjs.com/package/react-element-proptypes :

const ElementPropTypes = require('react-element-proptypes');

const Modal = ({ header, items }) => (
    <div>
        <div>{header}</div>
        <div>{items}</div>
    </div>
);

Modal.propTypes = {
    header: ElementPropTypes.elementOfType(Header).isRequired,
    items: React.PropTypes.arrayOf(ElementPropTypes.elementOfType(Item))
};

// render Modal 
React.render(
    <Modal
       header={<Header title="This is modal" />}
       items={[
           <Item/>,
           <Item/>,
           <Item/>
       ]}
    />,
    rootElement
);
1
wizardzloy

Il faut utiliser "React.isValidElement (child)" avec "child.type" si on travaille avec TypeScript afin d'éviter des erreurs d'incompatibilité de type.

React.Children.forEach(props.children, (child, index) => {
  if (React.isValidElement(child) && child.type !== Card) {
    error = new Error(
      '`' + componentName + '` only accepts children of type `Card`.'
    );
  }
});
1
Karna