Loving the v4 API currently! Wanted to start a thread on this so the community can stay updated. From the FAQ:
What about scrolling?
We have some code close to being published that will manage the scroll positions of window and individual elements.
Whose working on this, and whats the ETA for this? Will it still be provided by the react-router-scroll
library?
I have a POC on my machine, but it requires #3956 to be fixed before it works.
@mjackson it is fixed! just not released yet.
@ryanflorence Any updates on this? Scroll management would be good to have in a router :)
ScrollManagement on v4 still open or fixed? I to would my page to show the same after router.goBack()
As a temporary solution this SO Question + Accepted answer have worked for me: http://stackoverflow.com/questions/40217585/in-react-router-v4-how-does-one-link-to-a-fragment-identifier
@mjackson Are we deferring to the browsers on this or putting something together (or updating your POC)?
I'll see if I can push an updated POC soon.
Party pooper here. In case of next POC will take next several months, maybe this could help meanwhile: https://github.com/4Catalyzer/found-scroll
馃槀 @steida, you're such a helpful ray of sunshine. thank you!
Whoa, just looked at found and ... found some code that is eerily similar to my code. Creepy.
OK, I'm going to try and find an elegant solution to this problem this week for (at least the DOM bindings in) v4. Up to this point I've focused all my attention on making sure we had a good core, and now that we're shipping v4 betas every few days I think we're there.
If you're interested in trying to scratch your own itch and solve this problem for your app, but you've been hesitant because v4 was too unstable, you should feel free to give it a shot now too.
For folks who are interested, we've got quite a backstory that you can follow right here in this repo. Here are a few links that should help you get a feel for research that has already been done in previous versions:
<Route preserveScrollPosition>
API<Link>
s<Route preserveScrollPosition>
with the <Route scrollStrategy>
APIAnd for everyone who thinks "oh my gosh I can't believe they don't have an answer for this yet" I refer you to https://github.com/ReactTraining/react-router/issues/707#issuecomment-70522213:
Is this really such a mess?
Yes.
If you're in the middle of a project with complex scroll management requirements right now, I'd love to hear about it and/or see your proposed solutions. The v4 API is much more modular than past versions, so it should be a lot easier than it used to be to get in and alter how the router works simply by inserting components into the tree. I've got a POC that currently preserves the scroll position on document.body
, but some apps have more strenuous requirements than that.
/cc @gaearon @ryanflorence @taurose because reading through those old issues made me remember those days when we were just getting started on this project very fondly 馃挄
We decided to adopt react-router 4 around the second alpha and went to production at around alpha-5
The key stakeholders of our site were happy for us to land with a solution that simply scrolled to top on a forward navigation, which was very simple to implement with the v4 API. 馃憤
We are currently looking at updating to the beta and as part of that we will be looking at implementing the following scroll behaviors.
Upon navigating to a content page, scrolling to the content in the page (skipping header)
Ensuring on backwards navigation, with fresh state being used to render, the user is ether;
The v4 api has been a pleasure to work with so far.
Related: scroll restoration is coming to browsers through a proposed addition to the HTML5 history API. Something to keep in mind.
Poor man's scroll restoration:
On lwjgl.org we code-split (webpack 2) each route so we have to wrap each route component with https://github.com/LWJGL/lwjgl3-www/blob/master/client/routes/asyncRoute.js
It was natural to put the scroll restoration code there. Our approach is very simple and it uses the generated location.key to remember the scroll position. If we get a POP action we restore the previous scroll position otherwise we scroll to top. It works really well for a simple content website.
Caveats:
A cleaned-up version of the HOC can be found here: https://gist.github.com/apostolos/7001146c5437368eef2d281f9ecfb3c8
We're in the final week before launching a beta of our next app that relies heavily on RR4 (we've been confident enough in the RRv4 changes that warranted using a alpha/beta version in our app).
As mentioned above, the accepted solution in this question http://stackoverflow.com/questions/40217585/in-react-router-v4-how-does-one-link-to-a-fragment-identifier has been working perfectly for us - we've had no issues with it so far, and works well.
I understand its only a piece of the pie, as it addresses only the jump to a fragment identifier part of the problem, but is still useful regardless.
I wanted to jot down some thoughts on scroll management. I've implemented it several times as the router (and its beta releases) have changed over the last couple of years. I've implemented it as components outside of the router, inside of the router, inside of the history
lib, and with raw DOM.
I'm not sure we need to do anything about it (馃槀). Browsers are starting to handle scroll restoration with history.pushState
on their own in the same manner they handle it with normal browser navigation. It already works in chrome and it's really great.
Scroll Restoration Spec: https://majido.github.io/scroll-restoration-proposal/history-based-api.html#web-idl
Most of the time all you need is to "scroll to the top" because you have a long content page, that when navigated to, stays scrolled down. That's easy, this will scroll up your entire app on every navigation:
class ScrollToTop extends Component {
componentDidUpdate(prevProps) {
if (this.props.location !== prevProps.location) {
window.scrollTo(0, 0)
}
}
render() {
return null
}
}
const ScrollToTopOnNav = () => (
<Route component={ScrollToTop}/>
)
// Then render it at the top of your app
const App = () => (
<div>
<ScrollToTopOnNav/>
</div>
)
Or you could do it in just the components that need it when they mount:
class ScrollToTopOnMount extends Component {
componentDidMount(prevProps) {
window.scrollTo(0, 0)
}
render() {
return null
}
}
class LongContent extends Component {
render() {
<div>
<ScrollToTopOnMount/>
<h1>Here is my long content page</h1>
</div>
}
}
// somewhere else
<Route path="/long-content" component={LongContent}/>
For a generic API I was headed toward this:
<Router>
<ScrollRestoration>
<div>
<h1>App</h1>
<RestoredScroll id="bunny">
<div style={{ height: '200px', overflow: 'auto' }}>
I will overflow
</div>
</RestoredScroll>
</div>
</ScrollRestoration>
</Router>
First, ScrollRestoration
would scroll the window up on navigation. Second, it would use location.key
to save the window scroll position and the scroll positions of RestoredScroll
components to sessionStorage
. Then, when ScrollRestoration
or RestoredScroll
components mount, they could look up their position from sessionsStorage
.
What got tricky for me was defining an "opt-out" API for when I didn't want the window to scroll to be managed. For example, if you have some tab navigation floating inside the content of your page you probably don't want to scroll to the top (the tabs might be scrolled out of view!).
When I learned that chrome manages scroll position for us now, and realized that different apps are going to have different scrolling needs, I kind of lost the belief that we needed to provide something--especially when usually people just want to scroll to the top (which you saw is trivial to add to your app on your own).
So, yeah ... I'm not sure we need it but that API I just showed above would be pretty straightforward to implement (assuming you have a good wrapper around session storage!). I no longer feel strongly enough to do the work myself (plenty other things for me to work on!) but I'd love to help anybody who feels inclined. A solid solution would even live in this monorepo and be an official package. Hit me up if you get started on it :)
@ryanflorence Maybe we just add <ScrollToTop>
to react-router-dom
with some docs that link back to this issue and call it good?
I second @mjackson's opinion. <ScrollToTop>
component is enough, it will cover most of everyone's needs. If someone needs specific/non-default behaviour, there is high change it's project specific and RR has no change to cover that. As mentioned, browsers are starting to support scroll restoration API natively, we should make use of that instead.
This would make a great PR for someone who wants to go for it. Removing the 4.0.0 milestone because I don't consider this blocking for v4.
This is what we've been using for a few weeks now: https://gist.github.com/spicydonuts/d1ca3d78b9448004455562af1f04f81e
It's not real pretty but it behaves close to how you'd expect the browser to, all contained in one component. It also means other components which get location
passed in (withRouter
, Route
, etc) can inspect that scroll state.
Alright, doc is written so I can close this now :)
The docs look really awesome. Can I confirm that scrolling to a fragment identifier is outside of scope of the docs, the library and this issue?
For basic scroll to #hash-fragment
functionality I created a HashLink
component that you can install from npm (it wraps RRv4's Link
component). This is based on the solution for RRv2/3.
Live example: http://react-router-hash-link.rafrex.com
Repo: https://github.com/rafrex/react-router-hash-link
When you click on a link created with react-router-hash-link
it will scroll to the element on the page with with the id
that matches the #hash-fragment
in the link.
$ npm install --save react-router-hash-link
// In YourComponent.js
...
import { HashLink as Link } from 'react-router-hash-link';
...
// Use it just like a RRv4 link:
<Link to="/some/path#with-hash-fragment">Link to Hash Fragment</Link>
maybe it varies by browser, but with <BrowserRouter/>
hash links just work for me.
@ryanflorence, it doesn't seem to work for me in chrome/safari/firefox. I created a branch of the example site using Browser Router and the Link component to test it out. Can you give it a try and let me know if you get different results. Thanks.
https://github.com/rafrex/react-router-hash-link/tree/browser-router-link
$ git clone -b browser-router-link https://github.com/rafrex/react-router-hash-link.git
$ npm install
$ npm start
The site will be available on port 8080
In case anyone finds it useful, I created a React scroll management library to address the various issues mentioned here:
Please give it a try and submit issues/PRs if you find any issues:
https://github.com/trevorr/react-scroll-manager
It targets React 16 and supports React Router 4, though it's actually agnostic to the router (except that it indirectly uses the history
library underlying React Router). It's based on the ideas behind @ryanflorence's react-router-restore-scroll component for React Router 3 and @gajus's https://github.com/ReactTraining/react-router/issues/394#issuecomment-313120204 solution for hash link scrolling.
Most helpful comment
@ryanflorence Any updates on this? Scroll management would be good to have in a router :)