only allow children of a specific type in a react component

后端 未结 14 1652
天涯浪人
天涯浪人 2020-11-29 00:59

I have a Card component and a CardGroup component, and I\'d like to throw an error when CardGroup has children that aren\'t Card

相关标签:
14条回答
  • 2020-11-29 01:39
    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`);
      },
    
    }
    
    0 讨论(0)
  • To validate correct children component i combine the use of react children foreach and the Custom validation proptypes, so at the end you can have the following:

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

    As you can see is having and array with the name of the correct type.

    On the other hand there is also a function called componentWithName from the airbnb/prop-types library that helps to have the same result. Here you can see more details

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

    Hope this help some one :)

    0 讨论(0)
  • 2020-11-29 01:43

    I made a custom PropType for this that I call equalTo. You can use it like this...

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

    Now, MyParentComponent only accepts children that are MyChildComponent. You can check for html elements like this...

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

    Here is the implementation...

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

    You could easily extend this to accept one of many possible types. Maybe something like...

    React.PropTypes.equalToOneOf = function (arrayOfAcceptedComponents) {
    ...
    }
    
    0 讨论(0)
  • 2020-11-29 01:45

    For those like me, using the TypeScript version. You can filter/modify components like this:

    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;
            });
    
    0 讨论(0)
  • 2020-11-29 01:45

    Assert the type:

    props.children.forEach(child =>
      console.assert(
        child.type.name == "CanvasItem",
        "CanvasScroll can only have CanvasItem component as children."
      )
    )
    
    0 讨论(0)
  • 2020-11-29 01:45

    Considered multiple proposed approaches, but they all turned out to be either unreliable or overcomplicated to serve as a boilerplate. Settled on the following implementation.

    class Card extends Component {
      // ...
    }
    
    class CardGroup extends Component {
      static propTypes = {
        children: PropTypes.arrayOf(
          (propValue, key, componentName) => (propValue[key].type !== Card)
            ? new Error(`${componentName} only accepts children of type ${Card.name}.`)
            : null
        )
      }
      // ...
    }
    

    Here're the key ideas:

    1. Utilize the built-in PropTypes.arrayOf() instead of looping over children
    2. Check the child type via propValue[key].type !== Card in a custom validator
    3. Use variable substitution ${Card.name} to not hard-code the type name

    Library react-element-proptypes implements this in ElementPropTypes.elementOfType():

    import ElementPropTypes from "react-element-proptypes";
    
    class CardGroup extends Component {
      static propTypes = {
        children: PropTypes.arrayOf(ElementPropTypes.elementOfType(Card))
      }
      // ...
    }
    
    0 讨论(0)
提交回复
热议问题