web-dev-qa-db-de.com

nur Kinder eines bestimmten Typs in einer React-Komponente zulassen

Ich habe eine Card-Komponente und eine CardGroup-Komponente, und ich möchte einen Fehler auslösen, wenn CardGroup Kinder hat, die keine Card-Komponenten sind. Ist das möglich oder versuche ich, das falsche Problem zu lösen?

27
bigblind

Sie können die displayName für jedes Kind verwenden, auf das Sie über den Typ zugreifen können:

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

Für React 0.14+ und die Verwendung von ES6-Klassen sieht die Lösung folgendermaßen aus:

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

Sie können eine benutzerdefinierte propType-Funktion verwenden, um Kinder zu überprüfen, da Kinder nur Requisiten sind. Ich habe auch einen Artikel darüber geschrieben, wenn Sie mehr Details wünschen.

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

Ich habe dafür einen benutzerdefinierten PropType erstellt, den ich equalTo nenne. Sie können es so verwenden ...

class MyChildComponent extends React.Component { ... }

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

Jetzt akzeptiert MyParentComponent nur Kinder, die MyChildComponent sind. Sie können so nach HTML-Elementen suchen ...

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

Hier ist die Umsetzung ...

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

Sie können dies leicht erweitern, um einen von vielen möglichen Typen zu akzeptieren. Vielleicht so etwas wie ...

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

Sie können Ihrer Card-Komponente eine Requisite hinzufügen und dann in dieser CardGroup-Komponente nach dieser Requisite suchen. Dies ist der sicherste Weg, um dies in React zu erreichen.

Diese Requisite kann als defaultProp hinzugefügt werden, sodass sie immer vorhanden ist.

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

Die Überprüfung, ob die Kartenkomponente tatsächlich eine Kartenkomponente ist, mithilfe von type oder displayName ist nicht sicher, da sie während des Produktionsbetriebs nicht funktioniert, wie hier angegeben: https://github.com/facebook/react/issues/6167#issuecomment -191243709

2
Hedley Smith

Für diejenigen wie mich verwenden Sie die TypeScript-Version . Sie können Komponenten wie folgt filtern/modifizieren:

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

Um die korrekte untergeordnete Komponente zu validieren, kombiniere ich die Verwendung von reagiere children für alle und die Custom-Validierungsvorschläge , sodass Sie am Ende Folgendes haben können:

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

Wie Sie sehen können, ist ein Array mit dem Namen des richtigen Typs.

Auf der anderen Seite gibt es auch eine Funktion namens componentWithName aus der airbnb/prop-types-Bibliothek, die dazu beiträgt, dasselbe Ergebnis zu erzielen . Hier können Sie mehr Details sehen

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

Hoffe das hilft jemandem :)

1
Ismael Terreno

Verwenden Sie die React.Children.forEach-Methode, um über die untergeordneten Elemente zu iterieren, und verwenden Sie die name-Eigenschaft, um den Typ zu überprüfen:

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

Ich empfehle die Verwendung von Card.name anstelle von 'Card' string, um die Wartung und Stabilität in Bezug auf uglify zu verbessern.

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

1
Salim

Ich habe das Paket veröffentlicht, mit dem die Arten von React-Elementen überprüft werden können 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

Wenn Sie mit TypeScript arbeiten, müssen Sie "React.isValidElement (child)" zusammen mit "child.type" verwenden, um Fehler bei der Typkonvertierung zu vermeiden.

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

Für mich war der einfachste Weg, dies zu erreichen, das Befolgen von Code.

Beispiel 1:

import React, {Children} from 'react';

function myComponent({children}) {

  return (
    <div>{children && Children.map(children, child => {
      if (child.type === 'div') return child
    })}</div>
  )
}

export default myComponent;

Beispiel 2 - Mit Komponente

import React, {Children} from 'react';

function myComponent({children}) {

  return (
    <div>{children && Children.map(children, child => {
      if (child.type.displayName === 'Card') return child
    })}</div>
  )
}

export default myComponent;
0
Ballpin