typescript 使用 instanceof 测试 React 组件的基类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39387405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using instanceof to test for base class of a React component
提问by Bj?rn Boxstart
To support nested navigation menu's, we're using React.cloneElement to add properties to child menu components (the menu components are custom components, based on react-bootstrap). To prevent that we're cloning all elements even though they are not child menu components, but regular content components, I want make the cloning conditional.
为了支持嵌套导航菜单,我们使用 React.cloneElement 向子菜单组件添加属性(菜单组件是基于 react-bootstrap 的自定义组件)。为了防止我们克隆所有元素,即使它们不是子菜单组件,而是常规内容组件,我想让克隆有条件。
All menu components are sub classes of 'MenuBase' (which itself is a sub class of React.Component). In my code, I tried to test whether a child of a react component (reading this.props.children by use of the React.Children utility functions) is an instance of MenuBase.
所有菜单组件都是“MenuBase”的子类(它本身是 React.Component 的子类)。在我的代码中,我尝试测试 React 组件的子组件(通过使用 React.Children 实用程序函数读取 this.props.children)是否是 MenuBase 的实例。
Simplified code:
简化代码:
interface IMenuBaseProps {
// menu related properties
}
abstract class MenuBase<P extends IMenuBaseProps, S> extends React.Component<P, S> {
// constructor etc.
}
interface IGeneralMenuProps extends IMenuBaseProps {
// additional properties
}
class GeneralMenu extends MenuBase<IGeneralMenuProps, {}> {
public render(): JSX.Element {
// do some magic
}
}
Somewhere in the menu logic I want to do something like the following
在菜单逻辑的某个地方,我想做如下事情
React.Children.map(this.props.children, (child: React.ReactElement<any>): React.ReactElement<any> ==> {
if (child instanceof MenuBase) {
// return clone of child with additional properties
} else {
// return child
}
}
However, this test never results in true and as a result a clone is never made.
但是,此测试永远不会结果为真,因此永远不会进行克隆。
In the Chrome developer tools I can see that:
在 Chrome 开发人员工具中,我可以看到:
- child.type = function GeneralMenu(props)
- child.type.prototype = MenuBase
- child.type = 函数 GeneralMenu(props)
- child.type.prototype = MenuBase
Can somebody help me to clarify the following:
有人可以帮我澄清以下问题:
- Why is instanceof not working
- If I'm not able to use instance of the test for something in the inheritance chain of react components, what are my alternatives (I know I can test for the existence of one of the properties of IMenuBaseProps, but I don't really like that solution).
- 为什么 instanceof 不起作用
- 如果我不能将测试实例用于反应组件的继承链中的某些内容,那么我的替代方法是什么(我知道我可以测试 IMenuBaseProps 的属性之一是否存在,但我真的不喜欢该解决方案)。
回答by Nitzan Tomer
Looking at the definition file, the ReactChildren.mapis defined like this:
查看定义文件,ReactChildren.map定义如下:
map<T>(children: ReactNode, fn: (child: ReactChild, index: number) => T): T[];
This ReactChildis then defined like this:
然后这个ReactChild定义如下:
type ReactChild = ReactElement<any> | ReactText;
In your case it's probably ReactElementwhich is:
在您的情况下,它可能是ReactElement,它是:
interface ReactElement<P> {
type: string | ComponentClass<P> | SFC<P>;
props: P;
key?: Key;
}
Your MenuBase
is the type
, so it probably should be:
你MenuBase
是type
,所以它可能应该是:
React.Children.map(this.props.children, (child: React.ReactElement<any>): React.ReactElement<any> ==> {
if (child.type === MenuBase) {
// return clone of child with additional properties
} else {
// return child
}
}
It seems that the child.type
is the class of the component and not the instance and so instead of doing child.type instanceof MenuBase
you need to do child.type === MenuBase
.
似乎child.type
是组件的类而不是实例,因此child.type instanceof MenuBase
您无需执行此操作child.type === MenuBase
。
Edit
编辑
After playing around with things I came up with this solution:
在玩弄东西之后,我想出了这个解决方案:
React.Children.map(this.props.children, (child: React.ReactElement<any>): React.ReactElement<any> ==> {
if (MenuBase.isPrototypeOf(child.type)) {
// return clone of child with additional properties
} else {
// return child
}
}
If you'll want to check if the child
is GeneralMenu
then you'll need to do:
如果您想检查是否child
是,GeneralMenu
那么您需要执行以下操作:
child.type.prototype === GeneralMenu.prototype
回答by Bj?rn Boxstart
The solution of @NitzanTomer did not seem to work under all circumstances. I was not able to find the cause of the difference in test results on his machine and my machine.
@NitzanTomer 的解决方案似乎并非在所有情况下都有效。我无法找到他的机器和我的机器上测试结果差异的原因。
Finally I found the following solution:
最后我找到了以下解决方案:
public render(): JSX.Element {
// Iterate over children and test for inheritance from ComponentBase
let results: JSX.Element[] = React.Children.map(this.props.children, (child: React.ReactElement<any>, index: number): JSX.Element => {
let isComponent: boolean = typeof child.type !== 'string' && React.Component.prototype.isPrototypeOf((child.type as any).prototype);
let result: boolean = isComponent ? (child.type as any).prototype instanceof ComponentBase : false; // Not a component, then never a ComponentBase
return <li>
<div>Child nr. {index} is a React component: {isComponent ? "True" : "False"}</div>
<div>And is a sub class of ComponentBase: {result ? "True" : "False"} { result !== this.props.expectedTestResult ? "(unexpected)" : ""}</div>
</li>;
})
return (
<ul>
{results}
</ul>
)
}
In this solution, first a test is executed to check if the child is a React component. After that, 'instanceof' is used to determine whether the child component is a sub class of 'ComponentBase' (direct or indirect).
在此解决方案中,首先执行测试以检查子组件是否为 React 组件。之后,'instanceof' 用于确定子组件是否是 'ComponentBase' 的子类(直接或间接)。
Because in TypeScript the 'type' property of React.Component does not have a 'prototype' property a cast to 'any' is necessary.
因为在 TypeScript 中,React.Component 的 'type' 属性没有 'prototype' 属性,所以必须转换为 'any'。