'Route not rendering component

I'm trying to use react router in the following code, and render the CourseDetail component on button click. However, nothing renders (just goes to the appropriate URL).

I don't want to put it in my App.js file with all my other routes, as I want to use the props in this specific parent component. Any help is appreciated!

Also edit: ${match.url} gives /student/courses

import {
  BrowserRouter,
  Route,
  Switch,
  withRouter,
  Link,
} from "react-router-dom";
import { Card, Button } from "react-bootstrap";
import "./style.css";
import CourseDetail from "./CourseDetail";

class CourseItem extends React.Component {
  render() {
    const { course, match } = this.props;
    return (
      <div>
        <Card style={{ width: "18rem" }}>
          <Card.Body>
            <Card.Title>
              {course.subject} {course.code}: {course.name}
            </Card.Title>
            <Button variant="primary">
              <Link to={`${match.url}/${course.subject}${course.code}`}>
                Access Course
              </Link>
            </Button>
          </Card.Body>
        </Card>
        <BrowserRouter>
          <Switch>
            <Route
              exact
              path="/student/courses/:course"
              render={(props) => <CourseDetail {...props} course={course} />}
            />
          </Switch>
        </BrowserRouter>
      </div>
    );
  }
}

export default withRouter(CourseItem);

Course Item is rendered by Course List:

import React from "react";
import { withRouter } from "react-router-dom";
import Header from "../../../components/Header";
import CourseItem from "./CourseItem";
import { CardDeck, Container } from "react-bootstrap";
import axios from "axios";

class CourseList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      courses: [],
      isLoading: false,
      error: "",
    };
  }


  render() {
    const { courses } = this.state;
    return (
      <div className="course-list">
        <Header title="Courses & Labs" />
        <Container>
          <CardDeck>
            {courses &&
              courses.map((course) => {
                return <CourseItem course={course} />;
              })}
          </CardDeck>
        </Container>
      </div>
    );
  }
}

export default withRouter(CourseList);

App renders CourseList:

import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import data from "./data.json";
import Navigation from "./components/Navigation";
import Footer from "./components/Footer";
import Home from "./pages/Home";
import About from "./pages/About";
import Research from "./pages/Research";
import Publications from "./pages/Publications";
import HallOfFame from "./pages/HallOfFame";
import EvaluationList from "./pages/Student/TAEvaluations/EvaluationList";
import CourseList from "./pages/Student/Courses/CourseList";
import CourseDetail from "./pages/Student/Courses/CourseDetail";
import CourseEditorList from "./pages/Prof/CourseEditor/CourseEditorList";
import CourseEditorDetail from "./pages/Prof/CourseEditor/CourseEditorDetail";
import EvaluationEditorList from "./pages/Prof/EvaluationEditor/EvaluationEditorList";
import EvaluationEditorDetail from "./pages/Prof/EvaluationEditor/EvaluationEditorDetail";
import ReportList from "./pages/Prof/ReportEditor/ReportList";
import ReportDetail from "./pages/Prof/ReportEditor/ReportDetail";

class App extends React.Component {
  render() {
    const mockCourses = data.Courses;

    return (
      <div className="App">
        <BrowserRouter>
          <Navigation />
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
            <Route path="/research" component={Research} />
            <Route path="/publications" component={Publications} />
            <Route path="/halloffame" component={HallOfFame} />
 
            <Route exact path="/student/courses" component={CourseList} />
          </Switch>
        </BrowserRouter>
        <Footer />
      </div>
    );
  }
}

export default App;


Solution 1:[1]

Issue

<Route exact path="/student/courses" component={CourseList} /> means as soon as you push to a new route it no longer matches and thus doesn't render CourseList anymore which means the route for "/student/courses/:course" also isn't mounted and won't render anything.

Solution

CourseDetail will receive course as a prop off the route's match params given the path definition "/student/courses/:course".

props.match.params.course

Go ahead and define its route in the main router. Remove the exact prop and define the more specific path before the less specific path as the Switch returns only the first match.

class App extends React.Component {
  render() {
    const mockCourses = data.Courses;

    return (
      <div className="App">
        <BrowserRouter>
          <Navigation />
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
            <Route path="/research" component={Research} />
            <Route path="/publications" component={Publications} />
            <Route path="/halloffame" component={HallOfFame} />
 
            <Route
              path="/student/courses/:course"
              component={CourseDetail}
            />
    
            <Route path="/student/courses" component={CourseList} />
          </Switch>
        </BrowserRouter>
        <Footer />
      </div>
    );
  }
}

Remove the router from CourseItem, the main router can now handle that route/path.

class CourseItem extends React.Component {
  render() {
    const { course, match } = this.props;
    return (
      <div>
        <Card style={{ width: "18rem" }}>
          <Card.Body>
            <Card.Title>
              {course.subject} {course.code}: {course.name}
            </Card.Title>
            <Button variant="primary">
              <Link to={`${match.url}/${course.subject}${course.code}`}>
                Access Course
              </Link>
            </Button>
          </Card.Body>
        </Card>
      </div>
    );
  }
}

Refactor CourseDetail to pull the course from the match params versus props root. Implement the appropriate lifecycle functions to handle course prop value updating, for example, if user goes from "/student/courses/ABC" to "/student/courses/DEF" directly without first going to a different route/path.

class CourseDetail extends Component {

  ...

  componentDidMount() {
    const {
      match: {
        params: {
          course,
        },
      },
    } = this.props;
    // use `course` to load the correct course content
    
    ...
  }

  ...

  componentDidUpdate(prevProps) {
    const {
      match: {
        params: {
          course,
        },
      },
    } = this.props;

    const {
      match: {
        params: {
          course: prevCourse,
        },
      },
    } = prevProps;

    if (prevCourse !== course) {
      // use `course` to reload the correct course content
    }
    
    ...
  }
  
  ...
}

Note: if you can provide your CourseDetail component code I can provide a more accurate suggestion, the above is just a rough estimate.

Solution 2:[2]

in react-router-Dom 6, component is replaced by element, so instead of this:

 <Route path="/" component="App" />

use this:

<Route path="/" element={<App/>} />

Solution 3:[3]

What worked for me is:

  • I enclosed all my 'Route' components into one 'Routes' component. They are all imported from 'react-router-dom'

  • Also ensure you use the 'element' props to link to a component.

      import "./App.css";
      import { BrowserRouter, Route, Routes } from "react-router-dom";
      import Header from "./components/Header";
      import Homepage from "./pages/Homepage";
      import Coinpage from "./pages/Coinpage";
      import { makeStyles } from "@material-ui/core";    
      function App() {
      return (
      <BrowserRouter>
        <div>
          <Header />
          <Routes>
            <Route path="/" element={<Homepage/>} exact />
            <Route path="/coins/:id" element={<Coinpage/>} />
          </Routes>
        </div>
      </BrowserRouter>
    );
    }
    

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese
Solution 2
Solution 3