React - display specific content based on URL using useLocation

Viewed 703

Trying to teach myself react and stuck on one part... I can't seem to get page specific content to display based on URL using useLocation() -- HELP!

App.js - router displays page on click, yay!

    <Router>
        <Routes>
            <Route exact path="/" element={<Home />} />
            <Route path="/project/projectOne" element={<Project />} />
            <Route path="/project/projectTwo" element={<Project />} />
        </Routes>
    </Router>

Project.js - Project template serves up the components as expected

const Project = () => {
return (
    <div className='content-wrapper'>
        <Scroll />
        <ProjectIntro />
        <ProjectContent />
        <ProjectGrid />
        <Contact />
    </div>
); }; export default Project;

ProjectIntro.js - A component trying to serve up the content -- this is where I'm stuck, useLocation() see's the path, but I can't figure out how to show the "projectIntroDetails" based on that path.

const projectOne = () => {
  <h1 className='project-intro-heading'>Title Here</h1>,
  <figure className='project-intro-image'>
    <img src={projectImage} alt='placeholder'/>
  </figure> 
}

const projectTwo = () => {
    <h1 className='project-intro-heading'>Title Here</h1>,
    <figure className='project-intro-image'>
        <img src={projectTwoImage} alt='placeholder' />
    </figure>
}


const projectIntroDetails = {
    projectOne: {
        component: <projectOne />
    },
    projectTwo: {
        component: <projectTwo />
    }
}

const ProjectIntro = () => {
    const projectPath = useLocation();
    console.log(projectPath);

    // this is where I need help
    // how do I turn the path into seeing details to render the correct content?
    const projectIntroDetail = projectIntroDetails[projectPath.pathname.split("/project/")];

    return (
        <div className='project-intro'>
            {projectIntroDetail}
        </div>
    );

}; export default ProjectIntro;
2 Answers

You can use a component with a switch statement to determine which child component to render. This method allows you to pass any additional props to the child components.

If you don't need the <div className='project-intro'> element, you could also render the switch directly inside your ProjectIntro component.

const ProjectOne = () => {
  <h1 className='project-intro-heading'>Title Here</h1>,
  <figure className='project-intro-image'>
    <img src={projectImage} alt='placeholder'/>
  </figure> 
}

const ProjectTwo = () => {
    <h1 className='project-intro-heading'>Title Here</h1>,
    <figure className='project-intro-image'>
        <img src={projectTwoImage} alt='placeholder' />
    </figure>
}

const ProjectIntros = ({ slug, ...props }) => {
    switch(slug) {
        case 'projectOne':
            return <ProjectOne {...props} />;
        case 'projectTwo':
            return <ProjectTwo {...props} />;
        default:
            return null;
    }
}

const ProjectIntro = () => {
    const projectPath = useLocation();
    console.log(projectPath);

    return (
        <div className='project-intro'>
            <ProjectIntros slug={projectPath.pathname.split("/")[2]} />
        </div>
    );

}; export default ProjectIntro;

You don't really need to use the useLocation hook or pathname value to handle any conditional rendering logic, that's what the routing components are for.

I would suggest either passing in the correct sub-project component as a prop to be rendered on the correctly matching route, or refactoring the routes to do this in a more "react router" way.

Passing component down as prop example:

App

<Router>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route
      path="/project/projectOne"
      element={<Project projectIntro={<ProjectOne />} />}
    />
    <Route
      path="/project/projectTwo"
      element={<Project projectIntro={<ProjectTwo />} />}
    />
  </Routes>
</Router>

Project

const Project = ({ projectIntro }) => {
  return (
    <div className='content-wrapper'>
      <Scroll />
      <div className='project-intro'>
        {projectIntro}
      </div>
      <ProjectContent />
      <ProjectGrid />
      <Contact />
    </div>
  );
};

Using react-router-dom to your advantage.

Project

Convert Project into a layout component and render the ProjectOne and ProjectTwo components on nested routes. Layout routes are intended to be used to share common UI elements and layout, and render routed content into an outlet.

import { Outlet } from 'react-router-dom';

const Project = () => {
  return (
    <div className='content-wrapper'>
      <Scroll />
      <div className='project-intro'>
        <Outlet /> // <-- render nested routes here
      </div>
      <ProjectContent />
      <ProjectGrid />
      <Contact />
    </div>
  );
};

App

<Router>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/project" element={<Project />}>
      <Route path="projectOne" element={<ProjectOne />} />
      <Route path="projectTwo" element={<ProjectTwo />} />
    </Route>
  </Routes>
</Router>
Related