简体   繁体   中英

Filter React children by type with Typescript

I need to build a Wrapper for some other component and for that I try to filter React children by type:

// Component
const SubComponent = (props: SubComponentProps) => <NestedComponent {...props} />;

const MyComponent = (props: MyComponentProps) => {
    const { children } = props;
    const arrayChildren = React.Children.toArray(children);

    // doesn't work, but the type is right (child.type === SubComponent)
    const content = React.Children.count(arrayChildren) && arrayChildren.find((child) => React.isValidElement(child) && child.type === SubComponent);

    // works, but the type complains (child.type.name === "SubComponent")
    const content2 = React.Children.count(arrayChildren) && arrayChildren.find((child) => React.isValidElement(child) && child.type.name === "SubComponent");

    return (
        <Something {...props}>
            {/* Type complains, but works */}
            {content && <SomethingElse>{content.props.children}</SomethingElse>}
        </Something>
    );
};

MyComponent.SubComponent = SubComponent;

export default MyComponent;

// Usage
<MyComponent>
    <MyComponent.SubComponent>
        Some Stuff
    </MyComponent.SubComponent>
</MyComponent>

Why doesn't child.type match?

And why doesn't child.type.name exist on the type, even though it works? (The type of the child is given as React.ReactChild | React.ReactFragment | React.ReactPortal )

Thanks!

And why doesn't child.type.name exist on the type, even though it works? (The type of the child is given as React.ReactChild | React.ReactFragment | React.ReactPortal)

After you check for React.isValidElement(..) you basically narrow the type of child to React.ReactElement . And the issue with React.ReactElement is that it could have either a string or a function-contructor as the value to its type field. As you probably know, a string does not have a name property on it, thus you get an error. Actually, the expected constructor definition type provided by React typings is not considered as ordinary function as well (I guess it's something the lib authors could improve upon).

So I'd say, the simplest solution is just to cast the type to a function, like this:

(child.type as unknown as () => void).name === "SubComponent"

This will compile.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM