【发布时间】:2015-02-06 14:03:21
【问题描述】:
我有一个Card 组件和一个CardGroup 组件,当CardGroup 的子代不是Card 组件时,我想抛出一个错误。这可能吗,还是我试图解决错误的问题?
【问题讨论】:
标签: validation reactjs
我有一个Card 组件和一个CardGroup 组件,当CardGroup 的子代不是Card 组件时,我想抛出一个错误。这可能吗,还是我试图解决错误的问题?
【问题讨论】:
标签: validation reactjs
对于 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
}
}
【讨论】:
child.type === Card 在我的设置中不起作用。但是我通过使用child.type.prototype instanceof Card 让它工作了。我的 React 版本是 15.5.4
console.warn 或扔,因为这在oneOfType 内不起作用。
child.type 的文档在哪里?有人可以发链接吗?
您可以为每个孩子使用 displayName,通过 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");
}
}
【讨论】:
props.children 是opaque data type。更好地使用React.Children 实用程序,如here 所示。
name而不是displayName(最后一个对我不起作用)
您可以使用自定义 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>
);
}
});
【讨论】:
static propTypes = {}。
child.type.displayName 在混淆后不起作用
使用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
【讨论】:
对于那些使用 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;
});
【讨论】:
如果使用 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`.'
);
}
});
【讨论】:
我为此创建了一个自定义 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) {
...
}
【讨论】:
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 中实现这一目标的最安全方法。
这个 prop 可以作为 defaultProp 添加,所以它总是存在的。
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 或 displayName 检查 Card 组件是否确实是 Card 组件并不安全,因为它在生产使用期间可能无法正常工作,如下所示:https://github.com/facebook/react/issues/6167#issuecomment-191243709
【讨论】:
我发布了允许验证 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
);
【讨论】:
为了验证正确的子组件,我结合了react children foreach 和Custom validation proptypes 的使用,所以最后你可以得到以下内容:
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-types 库中还有一个名为 componentWithName 的函数,它有助于获得相同的结果。 Here you can see more details
HouseComponent.propTypes = {
children: PropTypes.oneOfType([
componentWithName('SegmentedControl'),
componentWithName('FormText'),
componentWithName('FormTextarea'),
componentWithName('FormSelect')
]).isRequired
};
希望这对某人有所帮助:)
【讨论】:
对我来说,实现这一目标的最简单方法是使用以下代码。
示例 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;
【讨论】:
考虑了多种提议的方法,但结果证明它们要么不可靠,要么过于复杂,无法用作样板。确定了以下实现。
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
)
}
// ...
}
以下是关键想法:
PropTypes.arrayOf() 而不是循环遍历子节点propValue[key].type !== Card 检查子类型${Card.name} 不硬编码类型名称库react-element-proptypes 在ElementPropTypes.elementOfType() 中实现了这一点:
import ElementPropTypes from "react-element-proptypes";
class CardGroup extends Component {
static propTypes = {
children: PropTypes.arrayOf(ElementPropTypes.elementOfType(Card))
}
// ...
}
【讨论】:
断言类型:
props.children.forEach(child =>
console.assert(
child.type.name == "CanvasItem",
"CanvasScroll can only have CanvasItem component as children."
)
)
【讨论】:
简单、生产友好的检查。在 CardGroup 组件的顶部:
const cardType = (<Card />).type;
然后,当迭代孩子时:
React.children.map(child => child.type === cardType ? child : null);
这项检查的好处在于,它还可以与库组件/子组件一起使用,这些库组件/子组件没有公开必要的类以使 instanceof 检查工作。
【讨论】: