React-spring: Nested routes

Created on 11 Apr 2018  路  8Comments  路  Source: pmndrs/react-spring

How would one go about having nested routes, whereas the first stage route (e.g. /users) triggers a transition where a navigation component and some content is shown. But once the user clicks on the navigation to go to a nested route, merely the content is transitioning.

I've tried multiple things, but whatever I do; the upper route changes too and thus the entire page keeps transitioning while I only want the nested part to transition. I'm pretty sure this happens due to my router configuration, but I cannot seem to figure out why.

Most helpful comment

Like you say, your issue is more a react-router issue; basically your top route is re-rendering when the child route is different, even though the parent should not re-render.

I've solved this (I believe) by changing what the parent switches on, basically, instead of keys={location.pathname} in the App component, I've done keys={location.pathname.split('/').slice(0, 2).join('/')} so that the parent only swaps out when that component does need to change.

https://codesandbox.io/s/2z0w19qk0j

All 8 comments

Can you make a very reduced codesandbox for this?

You're totally right. I was busy doing so by altering the Routing example, but it became a mess so I thought I just fired up the question first in quest for a clue. Shame on me. Hang in there, I'll get back to you tomorrow. Thanks a lot for responding.

Ok. I've reproduced the behaviour in a minimal fashion, not fully optimized, but I think it's clear what I'm trying to do here:

  • When landing a main route (e.g. /red) the transition is triggered and it renders the main route component.
  • From within this main route a Link is rendered which gives access to the subroute (e.g. /red/ultra)
  • When navigating to this sub route the transition for the mainroute is triggered as well, while I just want the sub route to transition.

I do realize that this is more react-router behaviour (triggering the location change for the parent route as well) than it is behaviour of react-spring, but I think this ticket is still in the right place because I bet more people will run into it. RIght? 馃槂

Like you say, your issue is more a react-router issue; basically your top route is re-rendering when the child route is different, even though the parent should not re-render.

I've solved this (I believe) by changing what the parent switches on, basically, instead of keys={location.pathname} in the App component, I've done keys={location.pathname.split('/').slice(0, 2).join('/')} so that the parent only swaps out when that component does need to change.

https://codesandbox.io/s/2z0w19qk0j

@danielterwiel @dominicglenn

Here's a fixed version: https://codesandbox.io/s/mjnwrk1o3p

The problems were:

  1. Make sure to use the native flag when your animated component contains complex subtrees. This lets React render only once, from then on the animation is done outside of React, mutating the dom directly. Previously it would render the whole route tree thousands of times, not sure if that confuses react-router but better not doing it if it can be prevented. It's just a slight adaption, but the gains are worth it, it will be way, way faster.

  2. Like Dominic said, the reason that everything would re-render on sub-route changes was the key you gave Transition:

<Transition keys={location.pathname} ...

The key would be different for /red and /red/ultra so it starts unmounting/mounting because it assumes theses are two components.

If you are more specific, like so:

<Transition keys={keys={location.pathname.split('/').filter(a => a)[0]}} ...

It would stay put if the main route is the same. I am not an expert when it comes to react-router, for certain they must have some api that does this more elegantly.

  1. Update to the latest version (4.0.5), i am not sure why this happened but Transition was called twice by the Route with similar props. The latest version implements shouldComponentUpdate. This was causing a slight glitch.

Thanks a whole lot to both of you. I clearly did not fully grasp the keys concept while it's actually pretty straight-forward.

I already had the native flag set in my application, but did not migrate it to the example. Thank you for pointing it out the significance of its impact on performance, I was not fully aware.

It's basically the same as any generic React key, for instance in a list <li key={something}>. The only difference is that Transition sets these by itself, and needs them beforehand. It may seem weird that you need a key even if you have only one element, but it's actually two, as Transition will keep React from unmounting right away so that animations can be applied to components that vanish (green->red, green is dead, but sticks around until it's animation concludes). At this point React would start screaming in your console if there weren't any keys.

Like you say, your issue is more a react-router issue; basically your top route is re-rendering when the child route is different, even though the parent should not re-render.

I've solved this (I believe) by changing what the parent switches on, basically, instead of keys={location.pathname} in the App component, I've done keys={location.pathname.split('/').slice(0, 2).join('/')} so that the parent only swaps out when that component does need to change.

https://codesandbox.io/s/2z0w19qk0j

This totally works! Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mkhoussid picture mkhoussid  路  3Comments

Oba-One picture Oba-One  路  3Comments

fortezhuo picture fortezhuo  路  3Comments

szahn picture szahn  路  3Comments

MaximeDenuit picture MaximeDenuit  路  4Comments