1

i am building an online database using react and the routes have been set up such that if user is not authenticated, they will be redirected to the login page.

However, i am facing difficulties for the nested routes. i think some parts of the implementation is incorrect.

2 problems:

  1. In order for the "404" (handled by NotFound component) page to work, exact props has to be used in <Route/>
  2. However if exact props is used, the <Links/> found in FixedDrawer component will not work. Clicking any of them will direct to "404" page.

What am i doing wrong here?

For simplicity's sake, i will only be sharing the rendering parts of the components.

Code as below:

App.js (PageShell is only used for animating page transitions)

import LoginPage from "./pages/LoginPage";
import Dashboard from "./pages/Dashboard";
import NotFound from "./pages/NotFound";
.
.
.
render() {
const childProps = {
  isAuthenticated: this.state.isAuthenticated,
  userHasAuthenticated: this.userHasAuthenticated
};

return (
  <div className="App">
    <Switch>
      <Route path="/login" exact component={PageShell(LoginPage)} />
      <Route path="/" exact component={PageShell(Dashboard)} />
      <Route component={NotFound} />
    </Switch>
  </div>
);

Dashboard.js

render() {
    return (
      <div>
        <NavBar user={this.state.user} />
        <FixedDrawer handleSetCategory={this.handleSetCategory} />
        <Switch>
          <Route path="/athletes" render={() => <AthletesDataTable />} />
          <Route path="/races" render={() => <RacesDataTable />} />
        </Switch>
      </div>
    );
  }

FixedDrawer.js

class FixedDrawer extends Component {
  constructor() {
    super();
    this.state = {};
  }

  render() {
    return (
      <Drawer variant="permanent" elevation={10}>
        <List component="nav" style={{ top: 65 }}>
          <ListItem button>
            <ListItemIcon>
              <FaceIcon />
            </ListItemIcon>
            <Link to="/athletes" style={{ textDecoration: "none" }}>
              <ListItemText primary="Athletes" />
            </Link>
          </ListItem>
          <ListItem button>
            <ListItemIcon>
              <GroupIcon />
            </ListItemIcon>
            <Link to="/teams" style={{ textDecoration: "none" }}>
              <ListItemText primary="Teams" />
            </Link>
          </ListItem>
          <ListItem button>
            <ListItemIcon>
              <DateRangeIcon />
            </ListItemIcon>
            <Link to="/meets" style={{ textDecoration: "none" }}>
              <ListItemText primary="Meets" />
            </Link>
          </ListItem>
          <ListItem button>
            <ListItemIcon>
              <PoolIcon />
            </ListItemIcon>
            <Link to="/races" style={{ textDecoration: "none" }}>
              <ListItemText primary="Races" />
            </Link>
          </ListItem>
          <ListItem button>
            <ListItemIcon>
              <AssignmentIcon />
            </ListItemIcon>
            <Link to="/results" style={{ textDecoration: "none" }}>
              <ListItemText primary="Race Results" />
            </Link>
          </ListItem>
        </List>
      </Drawer>
    );
  }
}
jaanhio
  • 733
  • 1
  • 5
  • 14

1 Answers1

1

"Link" component will try to match the exact route. Since, "/meets", "/teams" doesn't match the routes you provided, it will give 404 page.

return (
  <div className="App">
   <Navbar />
   <Sidebar />
   <Switch>
    <Route path="/login" exact component={PageShell(LoginPage)} />
    <Route path="/:val" component={PageShell(SomePage)} />
    <Route path="/" exact component={PageShell(Dashboard)} />
    <Route component={NotFound} />
  </Switch>
  <Footer />
  </div>
);

I'll suggest you write the route component as above. And in the componentDidMount of "SomePage" component, you can get the value of "val" as mentioned in the path above in "this.props.match" object and render the "SomePage" component as per the routes you want.

Ex: In the didMount function of SomePage:

componentDidMount(){
   this.setState({val: this.props.match.params.val})
}

render(){

  return(
   <div>
     {
     this.state.val === 'teams' ? <TeamsComponent /> : null
     }
     {
     this.state.val === 'meets' ? <MeetsComponent /> : null
     }
   </div>
  )
}
Ashish Shetty
  • 306
  • 1
  • 2
  • 8
  • I need the components to be rendered within the Dashboard enclosed by the sidebar and navbar. – jaanhio Apr 17 '18 at 11:39
  • Answer edited, as shown above you can update the SomePage (in your case the dashboard component)component by getting the value from the url, by keeping sidebar and navbar constant. – Ashish Shetty Apr 18 '18 at 09:31
  • I see! So SomePage serves as some sort of router? – jaanhio Apr 18 '18 at 10:05
  • It renders but the reason I chose to put the <Route/> within Dashboard is because I want the navbar and sidebar to be fixed while the main content/data tables will change. This solution only renders the table component itself and nothing else – jaanhio Apr 18 '18 at 11:38
  • In order to make the navbar, sidebar constant, you can put those components in the app.js rather than putting it on Dashboard. In this way, the navbar remains constant and the content changes as per your routes. I've edited the answer above – Ashish Shetty Apr 20 '18 at 07:05