Search code examples
javascriptreactjsmdbootstrapmdbreact

React dynamically using sub-components with props


Maybe there is a better way, but I try to make my React components as reusable as possible. So I am using a bootstrap Card and inside this Card I wanna place different components from outside (with props) dynamically. Without props it works fine. But with props I got an error message "Error: Cannot find module '../../pages/Dummy'".

This works perfect:

import React, {Suspense} from 'react';
import { MDBCard, MDBCardBody } from "mdbreact";

const Area = (props) => {

    const OtherComponent = React.lazy(() => import('../../pages/Dummy'));

    return (
            <MDBCard className="text-center">
                <MDBCardBody>
                    <Suspense fallback={<div>Loading...</div>}>
                        <OtherComponent/>
                    </Suspense>
                </MDBCardBody>
            </MDBCard>
    );
};

export default Area;

This doesn't work:

import React, {Suspense} from 'react';
import { MDBCard, MDBCardBody } from "mdbreact";

const Area = (props) => {

    const OtherComponent = React.lazy(() => import(props.compName));

    return (
            <MDBCard className="text-center">
                <MDBCardBody>
                    <Suspense fallback={<div>Loading...</div>}>
                        <OtherComponent/>
                    </Suspense>
                </MDBCardBody>
            </MDBCard>
    );
};

export default Area;

Call from outside:

<Area compName='../../pages/Dummy'/>

It's like I cannot lazy-load with props. Very strange.

Btw. I don't need to use lazy-loading if there is an easier way to use sub-components dynamically. I just thought this is the only way.


Solution

  • There are a few options:

    The first one uses 'slots,' which you can read about in the React docs here, or an article here.

    const DummyOne = React.lazy(() => import('../../pages/DummyOne');
    const DummyTwo = React.lazy(() => import('../../pages/DummyTwo');
    
    const Area = ({ comp }) => {
      return (
        <MDBCard className="text-center">
          <MDBCardBody>
            <Suspense fallback={<div>Loading...</div>}>
              {comp}
            </Suspense>
          </MDBCardBody>
        </MDBCard>
      );
    }
    
    // You dynamically pass the component itself into 'Area'
    <Area comp={<DummyOne />} />
    

    The second option is closer to what you have:

    const Area = ({ compName }) => {
      const components = {
        DummyOne: React.lazy(() => import('../../pages/DummyOne'),
        DummyTwo: React.lazy(() => import('../../pages/DummyTwo'),
      }
    
      const DynamicComponent = components[compName];
    
      return (
        <MDBCard className="text-center">
          <MDBCardBody>
            <Suspense fallback={<div>Loading...</div>}>
              <DynamicComponent />
            </Suspense>
          </MDBCardBody>
        </MDBCard>
      );
    }
    
    // You dynamically pass the component name to 'Area' as a string
    <Area compName="DummyOne" />