3

I have a nested route like this:

<Route path="modules" component={Modules}>
  <Route path=":moduleId" component={Module}/>
</Route>

I noticed that Module is mounting before Modules mounts. Is this the intended functionality of nested routes? I ask because I have some data gathering happening in Modules that needs to be resolved in order for Module to render as intended.

How is this possible if I can see that Modules has rendered before it even mounts?

connected_user
  • 814
  • 1
  • 9
  • 25

2 Answers2

5

This is not specific to react-router. In the React Component life-cycle:

  • Children are mounted before their parents. componentWillMount runs child-first.

  • Parents are unmounted before their children. componentWillUnmount runs parent-first.

Think about it this way: if you're constructing something, first you attach the smaller parts to the larger parts, progressively, until you have the complete thing. If you're dismantling it, first you remove the larger pieces, and then disassemble each of those pieces, until you're back to the components.

salezica
  • 74,081
  • 25
  • 105
  • 166
  • A parent `comonentWillMount` is called before a child `componentWillMount`. Children's `componentDidMount` methods are called before their parent's. – Paul S Dec 08 '16 at 17:59
  • The fact that a paren'ts componentWillMount is called before its child's componentWillMount makes it very tempting to do some data fetching in the parent (fetching the child's data). So I hope you can see why this all was confusing me. Basically before this nested route, my components had all their data fetched in the parent _Module_ component, which was then passed down to child _Modules_. But, because I want to implement shareable URL's I now need these child components to be routeParam-driven thus my arrival at this conundrum. – connected_user Dec 08 '16 at 18:47
4

What you are seeing is because react-router doesn't really use the React lifecycle process to render its children.

Instead, react-router individually renders each <Route>'s component and then merges them together. This is why you have to include this.props.children in a component route that has child routes. You can see the code responsible for this in the <RouterContext>'s render function.

Essentially what it does is that it takes the most nested route component and renders it. Then, it takes the next most nested component and renders that with the previously rendered component as its children prop. This continues down the line until you reach the root route component.

Given the routes:

<Route path="/" component={App}>
  <Route path="inbox" component={Inbox}>
    <Route path="messages/:id" component={Message} />
  </Route>
</Route>

When you visit /inbox/messages/12, react-router will do the following:

  1. Render <Message routeParams={{ id: 12 }} /> and store result as element.
  2. Render <Inbox children={element} /> and store result as element.
  3. Render <App children={element} /> and return result.
Paul S
  • 35,097
  • 6
  • 41
  • 38