ReactJS - Dynamic Component Name with closing tag and children elements

Viewed 242

New to React and I try to figure out if it's possible to have a dynamic element name with child components.

What I try to accomplish, is to create a site, that comes with connected/unconnected user area, using the same content with few differences when the user is connected.

To achieve that, I did to layout components like this:

//...
export default function PublicLayout(props) {
    return <>
        <Header />
        <HeaderMobile />

        {props.children}

        <Footer />
    </>;
}
import HeaderPrivate from "../app/components/partials/header-private";

export default function PrivateLayout(props) {
    return <>
        <HeaderPrivate />

        {props.children}
    </>;
}

So far I use the PublicLayout in my project like that:

export default function Home() {
    return <PublicLayout>
        {/*here are my child components*/}
    </PublicLayout>;
}

But I want to know if it's possible to have something like this:

export default function Home() {
    let connected = false;
    let Layout = connected ? 'PublicLayout' : 'PrivateLayout';

    return <Layout>
        {/*here are my child components*/}
    </Layout>;
}

I've seen several solutions for dynamic component names (all I've seen was for single components without children), but none of them worked for me.

I know that there's the other way around to accomplish my goal by doing the following, but if I could I have a dynamic component name it could be the best case for me.

This is the way to do it using an if statement:

export default function Home() {
    let connected = false;

    if ( connected ) {
        return <PrivateLayout>
            {/*here are my child components*/}
        </PrivateLayout>;
    }

    return <PublicLayout>
        {/*here are my child components*/}
    </PublicLayout>;
}

So, is there any way to accomplish this?

2 Answers

A common pattern (except using conditional rendering) is to map the name to its component via object:

import { PublicLayout, PrivateLayout } from "@layouts";

const LAYOUTS = {
  PublicLayout,
  PrivateLayout,
};

// layoutName = connected ? 'PublicLayout' : 'PrivateLayout'
export default function Home({ layoutName }) {
  const Layout = LAYOUTS[layoutName];

  return <Layout>{...}</Layout>;
}

Absolutely there is a way to dynamically set the component at runtime.

Choosing the Type at Runtime

Create a component map object to hold references to the imported components

const components = {
  PrivateLayout,
  PublicLayout
};

And then do something similar to what you tried by using the connected value to look up in the map the component to assign to the wrapper.

export default function Home() {
  const connected = false;
  const Layout = connected ? components.PublicLayout : components.PrivateLayout;

  return (
    <Layout>
      {/*here are my child components*/}
    </Layout>
  );
}
Related