L J
L J

Reputation: 111

How to correctly break apart routing to components in react-router-dom v4.3.1

I am looking to break apart my routing file into components so instead of

<BrowserRouter basename="/recruiter" >
        <div>
          <Nav />
          <Switch>
            /* HOMEPAGE ROUTES */
            <Route exact path="/" component={Home} />
            <Route exact path="/how-and-why" component={Pdf} />
            <Route path="/sign-in" component={SignIn} />
            <Route path="/investor-sign-in" component={SignIn} />
            <Route exact path="/sign-in-error" component={SignInError} /> 
            /* ADMIN ROUTES */
          {this.props.AdminUser ?
            <Route exact path="/welcome" component={LandingPage} />
            <Route exact path="/companies" component={CompaniesTable} />
            <Route exact path="/:companyId/edit-success" component={EditSuccess} />
            <Route exact path="/:companyId" component={SingleCompanyTable} />
            <Route exact path="/:companyId/:formId" component={SingleCompanyEdit} /> 
            ...10+ more Admin routes
            : <Redirect to="/" />
           }
          /*USER ROUTES */
           {this.props.regularUser ? 
              ...additoinal User routes 
             : null
            }
         </Switch>
      </div>
  </BrowserRouter>

It is

<BrowserRouter basename="/recruiter" >
        <div>
          <Nav />
          <Switch>
            <Homepage Routes />
            <AdminRoutes adminUser={this.props.adminUser} />
            <UserRoutes regularUser={this.props.regularUser} />
             //catch all
            <Route component={NotFound} />
    </Switch>
  </BrowserRouter>

With each Route component (such as AdminRoutes) looking like

export const AdminRoutes = (props) => {

  return props.adminUser ?
    <div>
       <Route exact path="/welcome" component={LandingPage} />
        <Route exact path="/companies" component={CompaniesTable} />
        <Route exact path="/:companyId/edit-success" component={EditSuccess} />
        <Route exact path="/:companyId" component={SingleCompanyTable} />
        <Route exact path="/:companyId/:formId" component={SingleCompanyEdit} /> 
        ...10+ more routes
    </div>
     : <Redirect to="/" />

In my understanding it should be the same render between the two options with the exception of the surrounding divs in the child component, but what happens is I can reach the routes in the first component, in this example HomepageRoutes, but none of the routes in AdminRoutes, UserRoutes etc.

I have had this previously working by wrapping sub components in switches, but then the catch all route/component (which is my 404 not found) are displayed all the time because of the contextual location change between the outer and inner switches.

I have read quite a bit on nested routes, but as far as I understand that involves wrapping the sub routes in another Route and the subroutes share the main route in their path

ie

const subRoute = () => {
  return (
    <Route path="/admin >
       /* this should work? */
      <Route path=/admin/:profile component={AdminProfile} />
      /*doesn't work? */
     <Route path="/add-company" component={AddCompany} />
   </Route>
)
}

I would like to avoid this if possible so I don't have to re-write all my routes that I'd like to 'component-ize'

Pertinent package.json info:

    "react": "^16.4.2",
    "react-bootstrap": "^0.32.1",
    "react-dom": "^16.4.2",
    "react-loadable": "^5.4.0",
    "react-redux": "^5.0.6",
    "react-router-bootstrap": "^0.24.4",
    "react-router-dom": "^4.3.1",
    "react-test-renderer": "^16.4.2",
    "redux": "^3.7.2",
    "redux-form": "^7.3.0",
    "redux-thunk": "^2.2.0"

Thank you in advance for your time and please let me know what other information to provide.

Upvotes: 1

Views: 2328

Answers (1)

Henry Woody
Henry Woody

Reputation: 15652

You need to wrap your top-level route controller components (HomepageRoutes, AdminRoutes , etc.) in Route components with path attributes in the top-level switch.

Also note that Switch matches the first Route that matches a path (see docs), so if your first Route has the path '/' without the exact attribute, it will always match so you won't get to any other Route. To handle this, you can put your Route for HomepageRoutes at the end of the Switch (but before NotFound).

So your BaseRouter should look like this:

<BrowserRouter basename="/recruiter" >
    <div>
      <Nav />
      <Switch>
        <Route path='/admin' render={ () => <AdminRoutes adminUser={this.props.adminUser}/> }/>
        <Route path='/users' render={ () => <UserRoutes regularUser={this.props.regularUser}/> }/>
        <Route path='/' component={ HomepageRoutes } />
         {/* catch all */}
        <Route component={NotFound} />
</Switch>

Note that any nested Route components need to include the full path. So the paths of the Routes nested within, say, AdminRoutes will need to begin with '/admin/'. From the documentation:

As soon as the app location matches the route’s path, your component will be rendered.

And I don't believe a Route or Switch knows whether or not it is nested.

For example if you have the location '/admin/welcome' but your Admin LandingPage only has path='/welcome', the path and app location do not match and the LandingPage component will not match. Instead the path should be '/admin/welcome'.


Edit for updated syntax:

Instead of using the render (or component) prop on the <Route> component, you can pass as children, which I find to be easier syntax to read and work with.

<BrowserRouter basename="/recruiter" >
    <div>
      <Nav />
      <Switch>
        <Route path='/admin'>
          <AdminRoutes adminUser={this.props.adminUser}/>
        </Route>

        <Route path='/users'>
          <UserRoutes regularUser={this.props.regularUser}/>
        </Route>

        <Route path='/'>
          <HomepageRoutes/>
        </Route>

        {/* catch all */}
        <Route>
          <NotFound/>
        </Route>
</Switch>

Upvotes: 1

Related Questions