I am wanting to create a dynamic route in my react app using react-router-dom. I have been reading documents over it but none of it is really making sense in my situation. I have a projects page, and then you can click on a link in the projects page and it takes you to a new page called project details. the url is different in each one.
App.js
<BrowserRouter>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/about" component={About} exact />
<Route path="/projects" component={Projects} exact />
<Route path="/workshops" component={Workshops} exact />
<Route path="/bodywork" component={Bodywork} exact />
<Route path="/contact" component={Contact} exact />
<Route path="/:projectdetails" component={ProjectDetails} exact />
</Switch>
</BrowserRouter>
there are ten different projects with all different names. they are in a data file like this:
export const ObjOne = {
title: 'Feldbewegung',
img: './images/bodyOfEarth.jpg',
alt: 'Feldbewegung',
link: '/feldbewegung-details'
};
export const ObjTwo = {
title: 'Nonmaterial city beautification',
img: './images/bodyOfEarth.jpg',
alt: 'Nonmaterial city beautification',
link: '/nonmaterial-city-beautification-details'
};
export const ObjThree= {
title: 'Body of Earth',
img: './images/bodyOfEarth.jpg',
alt: 'Body of Earth',
link: '/body-of-earth-details'
};
there is three for example. and they get passed into Projects.js
import { ObjOne, ObjTwo, ObjThree, ObjFour, ObjFive, ObjSix, ObjSeven, ObjEight, ObjNine, ObjTen} from '../components/Data/projectsData';
function ProjectImage({img, alt, link, title}) {
return (
<>
<div className="ProjectImage">
<img className="project-img" src={img} alt={alt} />
<a className="project-link" href={link}>{title}</a>
</div>
</>
)
}
function Projects({}) {
return (
<>
<Navbar1 />
<div className="page">
<Container className="projects-container">
<ProjectImage {...ObjOne} />
<ProjectImage {...ObjTwo} />
<ProjectImage {...ObjThree} />
...continuing to ObjTen..
is there a way to add dynamic routes or pages somehow?
There's multiple ways to handle this. They could be separate routes but they don't need to be, as they all use the same render function -- the difference is the props (title
, img
, etc.)
Instead of importing each object individually, let's use a import *
to group them together as properties of one object. This allows up to loop through them. It's also much more flexible if you decide to add or remove objects in the future, as changes will apply automatically.
import * as projects from '../components/Data/projectsData';
Your Projects
component can be simplified by looping through all available projects. We use Object.values(projects)
to the projects as an array
rather than a keyed object and then call the array .map
function.
function Projects() {
return (
<>
<Navbar1 />
<div className="page">
<div className="projects-container">
{Object.values(projects).map((project) => (
<ProjectImage
key={project.title} // items in a list need a unique key
{...project} // pass through all props of the project object
/>
))}
</div>
</div>
</>
);
}
We can create a ProjectDetails
component which can retrieve the data object for the current URL and then use those properties. We get the "/:projectdetails"
from the URL using the react-router useParams
hook (it can also be done through props).
export function ProjectDetails() {
const { projectdetails } = useParams();
// find the matching object from the array of projects
// note that the data uses a `/` at the start, but the params does not
const project = Object.values(projects).find(
(project) =>
project.link.toLowerCase().replace("/", "") === projectdetails.toLowerCase()
);
// need to handle any invalid urls
if (!project) {
// can use Redirect to redirect to a 404 page
return <h1>Error: Project Not Found</h1>;
}
return (
<div>
<h1>{project.title} Details</h1>
<ProjectImage {...project} />
</div>
);
}