user332336
user332336

Reputation: 91

How do I include react elements dynamically?

I have several elements whose names all end in "Page" and I'm passing as a prop an array of strings that contain the start of those names. I'm importing elements like HomePage, AboutPage, ContactsPage and I have available as a prop an array of strings that looks like: ['Home', 'About', 'Contact', ...] How can I map over the array and render the elements?

I tried string interpolation and I can produce the right names of the components, but the components themselves don't get rendered. My latest attempt was this:

import React from 'react';
import './Pages.scss';
import PropTypes from 'prop-types';
import HomePage from '../../pages/HomePage/HomePage';
import AboutPage from '../../pages/AboutPage/AboutPage';
import ProjectsPage from '../../pages/ProjectsPage/ProjectsPage';
import SkillsPage from '../../pages/SkillsPage/SkillsPage';
import LearningPage from '../../pages/LearningPage/LearningPage';

const Pages = ({ pages }) => {
  Pages.propTypes = {
    pages: PropTypes.array.isRequired,
  };

  return (
    <div className="Pages">
      {
        pages.map((page) => {
          const ComponentName = `${page}Page`;
          return <ComponentName />;
        })
      }
    </div>
  );
};

export default Pages;

I wanted to render the components , etc but instead I'm rendering a "ComponentName" component.

Upvotes: 1

Views: 150

Answers (2)

dance2die
dance2die

Reputation: 36895

You can dynamically load using import() or lazily import using React.lazy, which still requires the dynamic import().

I will show you how to load component lazily in this answer dynamically.

Steps required are,
1. Create a method that accepts a name of page and load dynamically. 2. Wrap the dynamically loaded Pages with <Suspense /> 3. Load page components dynamically

1. Create a method that accepts a name of page and load dynamically.

const loadPage = page => lazy(() => import(`./pages/${page}Page`));

You don't necessarily need to use lazy but the principle is the same. (You just don't need Suspense)

2. Wrap the dynamically loaded Pages with <Suspense />

const pageNames = ["Home", "About", "Contact"];
function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <Pages pages={pageNames} />
      </Suspense>
    </div>
  );
}

As you mentioned, you already have a list of pages you can pass to Pages so pass it to <Pages pages={pageNames} /> and wrap it with Suspense.

3. Load page components dynamically

This is where the magic and fun stuff happens 😀

function Pages({ pages }) {
  return (
    <ol>
      {pages.map(page => {
        const Page = loadPage(page);
        return (
          <li key={page}>
            <Page />
          </li>
        );
      })}
    </ol>
  );
}

From the step #1, we have loadPage, which loads a page component dynamically.

const loadPage = page => lazy(() => import(`./pages/${page}Page`));

Shown one more time as a reference.

For each page, you load a page, assigning it to Page (capitalize the first letter) and simply instantiate it (<Page />).

That's all it takes to load components dynamically.

You can find the working demo here.
Edit so.answer.57233711

Shameless Plug.

If you want to dig deeper, I have written two posts on how to load components dynamically.

  1. Loading React Components Dynamically on Demand
  2. Loading React Components Dynamically on Demand using React.lazy

Upvotes: 3

SIMDD
SIMDD

Reputation: 645

map component with componentName, then you can return corresponding correct

import React from 'react';
import './Pages.scss';
import PropTypes from 'prop-types';
import HomePage from '../../pages/HomePage/HomePage';
import AboutPage from '../../pages/AboutPage/AboutPage';
import ProjectsPage from '../../pages/ProjectsPage/ProjectsPage';
import SkillsPage from '../../pages/SkillsPage/SkillsPage';
import LearningPage from '../../pages/LearningPage/LearningPage';

const routerMap = {
  'pageabout': <AboutPage/>,
  'pagehome': <HomePage/>,
}

const Pages = ({ pages }) => {
  Pages.propTypes = {
    pages: PropTypes.array.isRequired,
  };

  return (
    <div className="Pages">
      {
        pages.map((page) => {
          const ComponentName = `${page}Page`;
          return routerMap[ComponentName ];
        })
      }
    </div>
  );
};

export default Pages;

Upvotes: 1

Related Questions