4

I have a react application with react-router. I am trying to setup nested routes:

"/" --> home page
"/products" --> products list (child component of home page)
"/products/new" --> new product: child component of products list

What I tried to do so far:

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

     <Route path="products" component="products" >

           <Route path="new" component="products_new" />
     </Route>

</Route>

Now in the browser from my default home page, when I hit "/products", the products component is loaded and I can see my list of products. But when I hit "products/new" nothing happens. I get a blank page. If I hit "/new" (not nested) it works (page product_new is loaded inside its parent). (this "/products/new" does not work; this "/new" works)

I had a look to this question on github Problem with nested routes #687. The solution says:

I discovered my problem. Parent routes are always called. Thats the intent. But child components need to have repeated <Router.RouteHandler/> to get rendered.

I cannot understand this solution. What does it mean: "but child components need to have repeated <Router.RouteHandler/> to get rendered"

EDIT1: Here are my components (routers and views):

  1. My routing hierarchy:

                <Route path="/" >    
                        <IndexRoute component={Home}/>
                        <Route path="products">
                            <IndexRoute component={Products} />
                            <Route path="new" component={Products_New} />
                        </Route>
                    </Route>                        
                </Router>
    
  2. My home component:

         <div className="col-lg-12">
            <h1>Home page</h1>
            <hr />
            {this.props.children}
        </div>
    
  3. My products components:

        <div>
            <div className="col-lg-12">
                <h1>Products</h1>
            </div>
            <hr />
            {this.props.children}
        </div>
    
  4. My product-new component:

enter image description here

TheSoul
  • 4,906
  • 13
  • 44
  • 74

1 Answers1

4

Have you had a look at IndexRoutes? If not, have a read about it on the official documentation. Your problem here is that when you visit /products/new, react-router tries to render all components that are above your products_new route.

This behavior is intended if you want to render a child component inside the parent component. Allow me to demonstrate with a couple of examples:

Example 1

Consider the following home component which has a Header and a Footer which is included on all pages.

<Header />
<div>{this.props.children}</div>
</Footer />

with the following routing:

<Route path="/" component={home} >
  <Route path="products" component={products} />
</Route>
  • Visiting / would render a page with just a <Header />, an empty <div> and <Footer />.
  • Visiting /products would render a page like above, but the <div> would now contain your <Products /> component.

Since, in your code you (probably) don't render {this.props.children} you will always get the parent <Home /> component, regardless if you visited / or /products.

This behavior is useful for stuff that wrap the main elements of your site, such as menus, banners, sidebars, etc.


Example 2

Now again consider the same home component:

<Header />
<div>{this.props.children}</div>
</Footer />

but with this routing:

<Route path="/">
  <IndexRoute component={home}
  <Route path="products" component={products} />
</Route>
  • Visiting / would render a page with just a <Header />, an empty <div> and <Footer />.
  • Visiting /products would now render your <Products /> component on its own, without being wrapped inside the parent <Home /> component.

TL;DR

If you instead want each route to render individual components, and not everything under the tree of that route, you should use the following routing instead:

const browserHistory = useRouterHistory(createHistory)({basename: '/'});

ReactDOM.render(
  <Route history={browserHistory}>
    <Route path="/">
      <IndexRoute component={home} />
      <Route path="products" >
        <IndexRoute component={products} />
        <Route path="new" component={products_new} />
      </Route>
    </Route>
  </Router>,
  document.getElementById('content')
);
Community
  • 1
  • 1
Chris
  • 57,622
  • 19
  • 111
  • 137
  • Thanks for this answer. I did what you told me but unfortunately it still does not work. I forgot to say that this is a SPA (single Page App) So I changed browserHistory to hashHistory and now it is working (only I have a # in my url... – TheSoul Jan 19 '17 at 14:30
  • Right, you would need to use a `history`. I strongly recommend using `browserHistory` though. I updated my code above, try that. – Chris Jan 19 '17 at 14:35
  • I understand your answer. My intent is to have every view rendered inside its parent. My problem is: why this url works: /products (by works I mean product-component is properly rendered inside home-component) But this url does not work /products/new (I get a blank page although there is a placeholder ( {this.props.children} ) inside products-components. I would expect to have products-new-component rendered inside products-component which in turn would be rendered inside home-component – TheSoul Jan 19 '17 at 15:06
  • I have edited my answer. Now you can see: - routing hierarchy - home component - products component - products-new component – TheSoul Jan 19 '17 at 15:18
  • Please put your new code in your question, and not as an answer. You need to remove your IndexRoute under `products` and change `products` route to ``. That will give the routing you want. – Chris Jan 19 '17 at 15:19
  • Your "official documentation" link is broken. – Tagc Apr 27 '17 at 12:27
  • @Tagc, indeed. Updated with the new link. Thanks!! – Chris Apr 27 '17 at 12:30