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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:49:55  来源:igfitidea点击:

Using instanceof to test for base class of a React component

reactjstypescript

提问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 开发人员工具中,我可以看到:

  1. child.type = function GeneralMenu(props)
  2. child.type.prototype = MenuBase
  1. child.type = 函数 GeneralMenu(props)
  2. child.type.prototype = MenuBase

typescript child watch

打字稿儿童手表

Can somebody help me to clarify the following:

有人可以帮我澄清以下问题:

  1. Why is instanceof not working
  2. 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).
  1. 为什么 instanceof 不起作用
  2. 如果我不能将测试实例用于反应组件的继承链中的某些内容,那么我的替代方法是什么(我知道我可以测试 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 MenuBaseis the type, so it probably should be:

MenuBasetype,所以它可能应该是:

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.typeis the class of the component and not the instance and so instead of doing child.type instanceof MenuBaseyou 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 childis GeneralMenuthen 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'。