只允许反应组件中特定类型的子项

时间:2014-12-08 20:13:15

标签: validation reactjs

我有一个Card组件和一个CardGroup组件,当CardGroup的子组件不是Card组件时,我想抛出一个错误。这是可能的,还是我试图解决错误的问题?

15 个答案:

答案 0 :(得分:41)

对于React 0.14+并使用ES6类,解决方案将如下所示:

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

答案 1 :(得分:17)

您可以为每个孩子使用displayName,可通过以下类型访问:

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

答案 2 :(得分:13)

您可以使用自定义propType函数来验证孩子,因为孩子只是道具。如果你想了解更多细节,我还在此写了article

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

答案 3 :(得分:4)

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 :(得分:3)

我为此制作了一个自定义PropType,我称之为equalTo。你可以像这样使用它......

class MyChildComponent extends React.Component { ... }

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

现在,MyParentComponent只接受MyChildComponent的孩子。你可以检查这样的html元素......

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

这是实施......

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

您可以轻松扩展此选项以接受多种可能类型中的一种。也许像是......

React.PropTypes.equalToOneOf = function (arrayOfAcceptedComponents) {
...
}

答案 5 :(得分:2)

您可以在<?php function foo($optionsgiven = array()) { $defaultoptions = ['width' => 10, 'height' => 20, 'weight' => 30]; foreach($defaultoption as $key => $value) { if(isset($optionsgiven[$key])) { $options[$key] = $optionsgiven[$key]; } else { $options[$key] = $value; } } } // main script $x = foo(['width' => 100]); ?> 组件中添加道具,然后在Card组件中检查此道具。这是在React中实现这一目标的最安全的方法。

这个道具可以作为defaultProp添加,因此它始终存在。

CardGroup

使用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> ) } } type检查卡组件是否确实是卡组件是不安全的,因为它在生产使用期间可能无效,如下所示:https://github.com/facebook/react/issues/6167#issuecomment-191243709

答案 6 :(得分:2)

对于像我这样的人,使用TypeScript版本。 您可以像这样过滤/修改组件:

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

答案 7 :(得分:2)

如果正在使用Typescript以避免类型不匹配错误,则必须使用“React.isValidElement(child)”和“child.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`.'
    );
  }
});

答案 8 :(得分:1)

我发布了允许验证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
);

答案 9 :(得分:1)

要验证正确的子组件,我结合使用react children foreachCustom validation proptypes,最后您可以拥有以下内容:

SELECT o.order_id, o.order_date, cu.customer_city, 
    cu.customer_first_name, cu.customer_last_name
  FROM orders o
  INNER JOIN customers cu 
    on o.customer_id = cu.customer_id

正如您所看到的那样,正在使用正确类型的名称和数组。

另一方面,airbnb / prop-types库中还有一个名为componentWithName的函数,它有助于获得相同的结果。 Here you can see more details

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

希望这对某人有所帮助:)

答案 10 :(得分:1)

使用React.Children.forEach方法遍历子对象,并使用name属性检查类型:

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

我建议使用Card.name代替'Card'字符串,以更好地维护和稳定 uglify

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

答案 11 :(得分:1)

考虑了多种提议的方法,但事实证明它们都不可靠或过于复杂,无法用作样板。解决了以下实现问题。

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

以下是关键思想:

  1. 使用内置的PropTypes.arrayOf()而不是遍历子对象
  2. 通过propValue[key].type !== Card在自定义验证器中检查子类型
  3. 使用变量替换${Card.name}不会硬编码类型名称

react-element-proptypesElementPropTypes.elementOfType()中实现此目的:

import ElementPropTypes from "react-element-proptypes";

class CardGroup extends Component {
  static propTypes = {
    children: PropTypes.arrayOf(ElementPropTypes.elementOfType(Card))
  }
  // ...
}

答案 12 :(得分:0)

对我来说,最简单的方法是遵循以下代码。

示例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;

示例2-具有组件

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;

答案 13 :(得分:0)

输入类型:

props.children.forEach(child =>
  console.assert(
    child.type.name == "CanvasItem",
    "CanvasScroll can only have CanvasItem component as children."
  )
)

答案 14 :(得分:0)

简单、生产友好的检查。在 CardGroup 组件的顶部:

const cardType = (<Card />).type;

然后,在迭代子项时:

React.children.map(child => child.type === cardType ? child : null);

这项检查的好处在于,它也适用于不公开使 instanceof 检查工作所需的类的库组件/子组件。