SuleymanSah
SuleymanSah

Reputation: 17858

React Router nested routing in the base url

I modified the sample given in React Router nested routing in the docs to setup nested routing in the base url, like this.

I want nested routing like /Home1 and /Home2 directly under the root url.

export default function NestingExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>
        <hr />
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  let { path, url } = useRouteMatch();
  console.log({url, path});

  return (
    <div>
      <h2>Homes</h2>
      <ul>
        <li>
          <Link to="/home1">Home 1</Link> 
          {/* also tried   <Link to={`${url}/home1`}>Home 1</Link> */}
        </li>
        <li>
        <Link to="/home2">Home 2</Link>
        </li>
      </ul>

      <Switch>
        <Route exact path={path}>
          <h3>Please select a home.</h3>
        </Route>
        <Route path={`${path}/home1`}>
          <Home1 />
        </Route>
        <Route path={`${path}/home2`}>
          <Home2 />
        </Route>
      </Switch>
    </div>
  );
}

function Home1() {
  return (
    <div>
      <h3>Home1 Content</h3>
    </div>
  );
}

function Home2() {
  return (
    <div>
      <h3>Home2 Content</h3>
    </div>
  );
}

No error displays in the browser, but Home1 page does not display. Isn't it possible to setup nested routing directly under the root?

Upvotes: 0

Views: 1829

Answers (4)

philip_nunoo
philip_nunoo

Reputation: 938

You would need to reorder your routes so the base route is the last route within the stack(using the strict keyword)

eg.


<Switch>
    <Route strict path="/users" component={UsersRoutes} />
    <Route strict path="/" compoent={PublicRoutes} />
</Switch>

And with this, each page can render their own Not Found pages.

eg.

// within PublicRoutes
<Switch>
    <Route exact path={path} component={() => <h1>Public Page</h1>} />
    <Route path="*" component={() => <h1>Not found</h1>} />
</Switch>

https://codesandbox.io/s/react-router-nesting-025cn?file=/example.js

Upvotes: 1

cpitt
cpitt

Reputation: 9

The problem is that react-router doesn't strip trailing slashes from path and url for you.

So at the root path: path === '/'

So when you concatenate that:

 <Route exact path={`${path}/home1`}>

You end up with

 <Route exact path="//home">

Which doesn't match any route

The easiest way to counter this is to normalize the path and url variables by removing the trailing slash

let { path, url } = useRouteMatch();
//strip trailing slashes
path = path.replace(/\/$/, '');
url = url.replace(/\/$/, '');

Your full example would be

export default function NestingExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>
        <hr />
        <Switch>
          <Route path="/">
            <Home />
          </Route>
          {/** Note That both "/" and "/home" will work */}
          <Route path="/home">
            <Home />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  let { path, url } = useRouteMatch();
  //strip trailing slashes
  path = path.replace(/\/$/, '');
  url = url.replace(/\/$/, '');

  return (
    <div>
      <h2>Homes</h2>
      <ul>
        <li>
          <Link to={`${url}/home1`}>Home 1</Link>
        </li>
        <li>
          <Link to={`${url}/home2`}>Home 2</Link>
        </li>
      </ul>

      <Switch>
        <Route exact path={${path}/}>
          <h3>Please select a home.</h3>
        </Route>
        <Route exact path={`${path}/home1`}>
          <Home1 />
        </Route>
        <Route exact path={`${path}/home2`}>
          <Home2 />
        </Route>
      </Switch>
    </div>
  );
}

function Home1() {
  return (
    <div>
      <h3>Home1 Content</h3>
    </div>
  );
}

function Home2() {
  return (
    <div>
      <h3>Home2 Content</h3>
    </div>
  );
}

Upvotes: 0

SuleymanSah
SuleymanSah

Reputation: 17858

I ended up creating a seperate /home route, and redirected / to /home to be able to use nested routing inside the home.

But I am still interested in a more clever answer.

export default function NestingExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>
        <hr />
        <Switch>
          <Route exact path="/">
            <Redirect to="/home" />
          </Route>
          <Route path="/home">
            <Home />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  let { path, url } = useRouteMatch();

  return (
    <div>
      <h2>Homes</h2>
      <ul>
        <li>
          <Link to={`${url}/home1`}>Home 1</Link>
        </li>
        <li>
          <Link to={`${url}/home2`}>Home 2</Link>
        </li>
      </ul>

      <Switch>
        <Route exact path={path}>
          <h3>Please select a home.</h3>
        </Route>
        <Route path={`${path}/home1`}>
          <Home1 />
        </Route>
        <Route path={`${path}/home2`}>
          <Home2 />
        </Route>
      </Switch>
    </div>
  );
}

function Home1() {
  return (
    <div>
      <h3>Home1 Content</h3>
    </div>
  );
}

function Home2() {
  return (
    <div>
      <h3>Home2 Content</h3>
    </div>
  );
}

Upvotes: 0

Vince V.
Vince V.

Reputation: 3143

I did not try this, but my guess is by using the "exact" keyword in the route of your top router, the nested structure does not match the url anymore. If you remove the property, does it work?

More information: https://reacttraining.com/react-router/web/api/Route/exact-bool

Upvotes: 0

Related Questions