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

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

88098 次浏览

您可以为每个子级使用 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");
}
}

您可以使用自定义 proType 函数来验证子级,因为子级只是道具。如果你想知道更多细节,我还写了一个 文章

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

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

我做了一个定制的 PropType,我称之为 equalTo。你可以像这样使用它..。

class MyChildComponent extends React.Component { ... }


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

现在,MyParentComponent只接受 MyChildComponent的子元素。

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

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


}

您可以向 Card组件添加一个道具,然后在 CardGroup组件中检查该道具。这是在 React 中实现这一目标的最安全的方法。

这个道具可以作为默认道具添加,所以它总是在那里。

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

使用 typedisplayName检查 Card 组件是否确实是 Card 组件是不安全的,因为它可能在生产使用期间无法工作,如下所示: https://github.com/facebook/react/issues/6167#issuecomment-191243709

对于使用 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;
});

为了避免类型不匹配错误,必须使用“ 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`.'
);
}
});

为了验证正确的子组件,我结合使用 孩子们的反应自定义验证原型,因此在最后你可以有以下几点:

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

正如您可以看到的,它使用正确类型的名称来拥有和数组。

另一方面,airbnb/prop- 类型库中还有一个名为 Component entWithName 的函数,它可以帮助获得相同的结果。 这里你可以看到更多的细节

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

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

使用 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'字符串,以便更好地维护和稳定 丑陋

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

断言类型:

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

考虑了多种提议的方法,但结果都不可靠或过于复杂,无法作为样板。决定采用以下实现方式。

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}不硬编码类型名称

反应元件原型机ElementPropTypes.elementOfType()中实现了这一点:

import ElementPropTypes from "react-element-proptypes";


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

一个简单的,生产友好的检查。在您的 CardGroup 组件的顶部:

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

然后,在对孩子们进行迭代时:

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

这个检查的好处是,它还可以处理库组件/子组件,这些组件不会公开必要的类来使 instanceof检查工作。