-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[changed] Remove preserveScrollPosition, add scrollStrategy #326
Conversation
This removes support for `preserveScrollPosition` due to its unforunate naming. Instead, we introduce three scrolling strategies: * `none`: Router doesn't scroll the window when routes change * `scrollToTop` (default): Router always scrolls to top when routes change * `imitateBrowser`: Router tries to act like browser acts with server-rendered pages: it scrolls to top when clicking on links, but tries to restore position when navigating back and forward You can only specify these on <Routes />. Per-route overrides are not supported, but you can supply a custom strategy object. This also fixes remix-run#252. Migration path: The default changed from what corresponded to `imitateBrowser`, to `scrollToTop`. If router's server-rendered scrolling imitation worked well for you, you must now specify it explicitly: ``` // before <Routes> <Routes preserveScrollPosition={false}> // after <Routes scrollStrategy='imitateBrowser'> ``` If you wish router to not try to manage scrolling, you must opt out: ``` // before <Routes preserveScrollPosition={true}> // after <Routes scrollStrategy='none'> ``` Also, as a third option, you may now use the simple `scrollToTop` strategy.
da4b134
to
47f0599
Compare
Actually now that #252 is fixed here, I don't mind having |
I'd personally rather we use My reasoning is that a bug with this code is going to be pretty much impossible for most people to realize its us doing it. When we know its solid, I whole-heartedly want it to be the default. @mjackson, thoughts? |
Also, great work :D |
@gaearon This is really fantastic work. Thank you so much! Unfortunately I've got a branch going right now that removes the No worries though. I say let's go ahead and merge this and then I'll do the merge manually. I'll try not to break your stuff ;) |
[changed] Remove preserveScrollPosition, add scrollStrategy
Thank you a lot of merging it quickly. Regarding the default.. I set it to As for my suggestion of making Finally, do you think |
So we can no longer specify that we don't want the browser to scroll on a per route basis? We have a lot of pages that when you transition from a route to a nested route, the page doesn't scroll, but the URL changes using repalceWith. For instance when you have a search form and then display search results on the same page (URL changes for deep linking, no scrolling needed). Once you have selected a search result, you then want to "check-out" and use the standard browser behavior. This was working great. What do you suggest we accomplish the same behavior using this new setup? Simplified router for example:
|
A little background: After screwing around with per-route scrolling it wasn't sufficient. Sometimes you wanted to scroll when leaving a route, other times you wanted to scroll when entering a route, and other times it depends on the action (using forward/back buttons v. clicking on a new link). Its not documented (yet) but you can supply your own scrolling behavior. <Routes scrollBehavior={YourScrollBehavior}/> Check out the ones that ship with the router Put whatever logic you need in there, you may need a second module that your components send data to in |
Thanks Ryan! I'll work on it and see if I can get and example together on how the old functionality can be implemented with the new router. I have have a few uses cases for it so I'm sure others will want it as well.
|
I don't think it should be too difficult to add back the ability to dictate scrolling behavior on a per-route basis. I honestly didn't think anyone would miss it tho. |
Are you sure it was working correctly before? Say I have /search that I don't want to scroll when query changes. With per-Route API, I would give it scrollBehavior=none. But I do want to restore the position when I open product from search and hit back. Similarly, I do want to scroll to top if I go to /search from /about. What I'm trying to say is I can't see how per-Route behaviors could make sense at all. Behavior always depends on both source and target routes, or it won't be consistent. |
Maybe @iamrandys needs a way to just cancel the default scrolling behavior on certain transitions? |
I see where you're going! Indeed, it looks like his use case is to opt out of any scrolling behavior when transitioning in the bounds of a particular route. Using this example, <Routes location="history">
<Route handler={App}>
<Route name="search" path="/search.html" handler={SearchPage}>
<Route name="select" path="/select.html" handler={SearchResults} noScroll />
</Route>
<Route name="price" path="/price.html" handler={PricePage} />
<Route name="purchase" path="/purchase.html" handler={PurchasePage} />
<Route name="confirmed" path="/confirmed.html" handler={ConfirmedPage} />
</Route>
</Routes> Say we have <Routes location="history">
<Route handler={App}>
<Route name="home" path="/" handler={HomePage} noScroll>
<Route name="feed" path="/" handler={FeedPage} />
<Route name="discover" path="/discover" handler={DiscoverPage} />
</Route>
<Route name="profile" path="/profile" handler={ProfilePage} />
</Route>
</Routes> Say Does this make sense? This is much easier to implement than arbitrary nesting of scroll behaviors, and it should cover most (if not all) use cases when you want scroll not to happen. |
There is also an option of using transition hooks and something like |
|
@rpflorence Yup, descendant or itself (when on a leaf route). |
<Routes location="history">
<Route handler={App}>
<Route name="A" noScroll>
<Route name="A1" />
<Route name="A2" />
</Route>
<Route name="B" noScroll />
<Route name="C">
<Route name="C1" />
<Route name="C2" />
</Route>
</Route>
</Routes> Scrolling behavior doesn't get called for:
Scrolling behavior gets called for:
|
I don't get that part. |
^^^ see comment above |
ok |
:P |
So what I'm suggesting is |
the tab navigation use-case is a great example. I have some doubt that I can't quite surface about being a couple levels deep in a noScroll, and wonder if it should only apply to the immediate children |
Can we warn if you define If we only apply behavior to immediate children, adding nested routes kinda breaks your app in unexpected ways. |
Another way to explain this is that |
Scrolling behavior gets called for:
|
Because these are different sections altogether. If A has Now that you're in Search (B), your path changes every type you type something (you transition to B again). We don't want to scroll to top, so that's why B has But when you go from Search (B) to About Us (C), we want to scroll. That was my point: |
<Routes location="history">
<Route handler={App}>
<Route name="home" path="/" handler={HomePage} noScroll>
<Route name="feed" path="/" handler={FeedPage} />
<Route name="discover" path="/discover" handler={DiscoverPage} />
</Route>
<Route name="search" path="/search" handler={SearchResultsPage} noScroll />
<Route name="about" path="/about" handler={AboutPage} />
</Route>
</Routes> By default, scroll behavior is performed on all transitions (red arrows). Routes can create “scrolling realms” for themselves and their descendants by specifying Transitions that are fully confined to a single scrolling realm, don't have any scroll behavior performed (blue arrows). If transition is not fully confined to a scrolling realm, |
@gaearon Ah, yes. Thank you for the art. That makes a lot of sense. @iamrandys Does this work for you? The case you're most concerned about works. |
Ah, that makes sense. I like it. Then I'll only have to add the noScroll in one place. Oh, I really like this. I will be able to remove a few preserveScrollPosition tags. Will be much cleaner. |
@iamrandys And if you need to preserve position between tabs (air | hotel | car) as well as search results, they just need to be placed under same container route that has |
Yes, which makes perfect sense, since they are all inside the same component. And then all of the other pages will be outside the noScroll route. Which again makes perfect since, because they will look like a completely different pages and don't share the same container. It looks like it all works out. Sweet, this is going to be really nice! Working with the react-router has been really fun. Let me know if I can do anything to help. |
@gaearon @iamrandys If either of you are up for making a PR around this, I would really appreciate it! Any takers? :D |
@mjackson I can definitely try this weekend! |
My only requirement is that the PR includes the artwork. |
This removes support for
preserveScrollPosition
due to its unforunate naming.Instead, we introduce three scrolling strategies:
none
: Router doesn't scroll the window when routes changescrollToTop
(default): Router always scrolls to top when routes changeimitateBrowser
: Router tries to act like browser acts with server-rendered pages: it scrolls to top when clicking on links, but tries to restore position when navigating back and forwardYou can only specify these on
<Routes />
only.Per-route overrides are not supported, but you can supply a custom strategy object.
This also fixes #252.
Migration path:
The default changed from what corresponded to
imitateBrowser
, toscrollToTop
.If router's server-rendered scrolling imitation worked well for you, you must now specify it explicitly:
If you wish router to not try to manage scrolling, you must opt out:
Also, as a third option, you may now use the simple
scrollToTop
strategy.