React - how to determine if a specific child component exists?

Viewed 15670

If I have this structure:

const MyComponent = (props) => {
    return (
        <Wrapper />{props.children}</Wrapper>
    );
}

and I use it like this:

<MyComponent>
    <SomeInnerComponent />
</MyComponent>

How can I check to see if <SomeInnerComponent /> has specifically been included between <MyComponent></MyComponent>, from within the MyComponent function?

3 Answers

Just want to provide an answer for a similar but different need, where you might have an HOC wrapping several layers of components, and you'd like to see if the HOC has already wrapped a component. The method I came up with was to have the HOC add a data-attribute onto the component to serve as a flag, which the HOC could then check on subsequent runs.

const WithFoo = Component = props => {
  return props["data-with-foo"]
    ? <Component {...props} />
    : (
      <FooWrapper>
        <Component {...props} data-with-foo />
      </FooWrapper>
    );
};

React nodes have a type property that's set to the constructor, or the function that created the node. So I wrote the following function for checking if a React node was created by a certain component.

const reactNodeIsOfType = (node, type) =>
    typeof node === "object" && node !== null && node.type === type;

Usage:

const MyComponent = (props) => {
    let hasInnerComponent = false;

    React.Children.forEach(props.children, (child) => {
        if (reactNodeIsOfType(child, SomeInnerComponent)) {
            hasInnerComponent = true;
        }
    });

    return (
        <>
            <div>hasInnerComponent: {hasInnerComponent ? "yes" : "no"}.</div>
            <Wrapper>{props.children}</Wrapper>
        </>
    );
}
<MyComponent><div /></MyComponent>
// hasInnerComponent: no

<MyComponent><SomeInnerComponent /></MyComponent>
// hasInnerComponent: yes

const c = <SomeInnerComponent />;
console.log(reactNodeIsOfType(c, SomeInnerComponent));
// true

This is the TypeScript version. Calling it will also assign the correct type to the node's props.

const isObject = <T extends object>(value: unknown): value is T =>
    typeof value === "object" && value !== null;

const reactNodeIsOfType = <
    P extends object,
    T extends { (props: P): React.ReactElement | null | undefined }
>(
    node: React.ReactNode,
    type: T
): node is { key: React.Key | null; type: T; props: Parameters<T>[0] } =>
    isObject<React.ReactElement>(node) && node.type === type;
Related