Reputation: 91
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
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
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
)
<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.
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.
If you want to dig deeper, I have written two posts on how to load components dynamically.
Upvotes: 3
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