假设您有以下组件接受一个或多个子JSX.Elements,并在使用React.cloneElement(child, { onCancel: () => {} }
呈现它们时将额外的回调传递给它们。
有问题的组件(摘录):
interface XComponentProps {
onCancel: (index: number) => void;
}
class XComponent extends React.Component<XComponentProps> {
render() {
const { onCancel } = this.props;
const children = typeof this.props.children === 'function' ?
React.cloneElement(this.props.children, { onCancel: () => onCancel() }) :
this.props.children.map((child, idx) => (
React.cloneElement(child, { onCancel: () => onCancel(idx) })
));
return <div>{children}</div>;
}
}
有问题的组件(摘录):
interface UserComponentProps { caption: string }
const UserComponent = (props: UserComponentProps) => (
<button onClick={props.onClose}>{props.caption || "close"}</button>
);
ReactDOM.render(
<XComponent onCancel={handleCancel}>
<UserComponent />
<UserComponent />
<UserComponent />
</XComponent>
);
现在TSC抱怨UserComponent
在其道具中没有onCancel
&#39;接口定义,确实没有。最简单的解决方法是手动定义onCancel
到UserComponentProps
接口。
但是,我想在不修改子节点的prop定义的情况下修复它,以便组件可以接受任意一组React元素。在这种情况下,有没有办法定义返回元素的类型,这些元素在XComponent
(父级)级别传递了额外的隐式道具?
答案 0 :(得分:1)
这是不可能的。无法静态地知道UserComponent从ReactDOM.render上下文中的父XComponent接收的道具。
如果您需要类型安全的解决方案,请将子项用作函数:
以下是XComponent
定义
interface XComponentProps {
onCancel: (index: number) => void;
childrenAsFunction: (props: { onCancel: (index: number) => void }) => JSX.Element;
}
class XComponent extends React.Component<XComponentProps> {
render() {
const { onCancel } = this.props;
return <div>{childrenAsFunction({ onCancel })}</div>;
}
}
现在您可以使用它来呈现UserComponent
<XComponent onCancel={handleCancel} childrenAsFunction={props =>
<span>
<UserComponent {...props} />
<UserComponent {...props} />
</span>
} />
这将很好地工作,我经常使用这种模式没有麻烦。
您可以重构XComponentProps以使用相关部分(此处为childrenAsFunction
函数)键入onCancel
道具。
答案 1 :(得分:0)
您可以使用接口继承(请参阅Extending Interfaces)并让UserComponentProps扩展XComponentProps:
interface UserComponentProps extends XComponentProps { caption: string }
这将为UserComponentProps提供XComponentProps的所有属性以及它自己的属性。
如果您不想要UserComponentProps定义XComponentProps的所有属性,您也可以使用Partial类型(参见Mapped Types):
interface UserComponentProps extends Partial<XComponentProps> { caption: string }
这将为UserComponentProps提供XComponentProps的所有属性,但使它们成为可选项。
答案 2 :(得分:0)
即使在JSX中,您也可以将子项作为函数传递。通过这种方式,您将获得正确的输入方式。 UserComponents道具界面应该扩展ChildProps。
interface ChildProps {
onCancel: (index: number) => void;
}
interface XComponentProps {
onCancel: (index: number) => void;
children: (props: ChildProps) => React.ReactElement<any>;
}
class XComponent extends React.Component<XComponentProps> {
render() {
const { onCancel } = this.props;
return children({ onCancel })};
}
}
<XComponent onCancel={handleCancel}>
{ props => <UserComponent {...props} /> }
</XComponent>
答案 3 :(得分:0)
我知道这已经解决,但是另一个解决方案是限制可以传递的子代类型。限制了包装器的功能,但可能是您想要的。
因此,如果您保留原始示例:
interface XComponentProps {
onCancel: (index: number) => void;
children: ReactElement | ReactElement[]
}
class XComponent extends React.Component<XComponentProps> {
render() {
const { onCancel } = this.props;
const children = !Array.isArray(this.props.children) ?
React.cloneElement(this.props.children, { onCancel: () => onCancel() }) :
this.props.children.map((child, idx) => (
React.cloneElement(child, { onCancel: () => onCancel(idx) })
));
return <div>{children}</div>;
}
}
interface UserComponentProps { caption: string }
const UserComponent: React.FC = (props: UserComponentProps) => (
<button onClick={props.onClose}>{props.caption || "close"}</button>
);
ReactDOM.render(
<XComponent onCancel={handleCancel}>
<UserComponent />
<UserComponent />
<UserComponent />
</XComponent>
);