Dynamic route react-router-dom

1.5k Views Asked by At

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?

1

There are 1 best solutions below

3
On BEST ANSWER

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>
  );
}

CodeSandbox Link