'React.js: Stop render from scrolling to top of page
Every time you perform a render in React.js, the UI scrolls to the top of the page.
JSFiddle: http://jsfiddle.net/bengrunfeld/dcfy5xrd/
Any nifty or reactive way to stop that?
E.g. If a User scrolls down the page, then pushes a button which causes a Render, the UI would stay in the same scroll location as before.
// Forces a render which scrolls to top of page
this.setState({data: data});
UPDATE: Why does the UI scroll to the top for some renders, but not others?
Solution 1:[1]
@floydophone answered this one on Twitter.
https://twitter.com/floydophone/status/608129119025561600
@bengrunfeld @reactjs you forgot
preventDefault()
on your link handlers
Solution 2:[2]
Ok if anyone read this , in my case the problem wasn't any of above. You must try first suggestions on above any way. I did everything including preventDefault()
but didn't help me. The problem was ; using styled-components
. Because, styled-components give a random classname every render. So it resets scroll. I gave the class name from css and solved my problem.
Solution 3:[3]
Wanted to add here for those who are not using anchor tags, preventDefault
will not save you. As bizarre as it is, for me it was related to rendering my child components inside a div
with
display:table > display:tablecell
For some reason the child component's scroll position is lost in this situation when there is any re-render. Problem vanished when I switched to flexbox (display:flex).
Solution 4:[4]
If render trigger scrolling to the top it usually means some UI component is changing its dimension because of the state, this can be fixed by adding a minimum width/height
Solution 5:[5]
This could be happening because of the change in redux state, which will show the changes everywhere in the code where it is getting used. Try making the change using the local state of that component on action of which, the page is getting scrolled.
Solution 6:[6]
2022 Hacky Solution:
I couldn't find a way to completely prevent the scrolling on the re-render. preventDefault
didn't help, and I didn't have access to the specific element structure, but I did have the className of the scrolling element.
useEffect(() => {
const el = document.getElementsByClassName('my-classname')?.[0];
const newTimeoutRef = setTimeout(() => el?.scrollTo(0, scrollTop), 50);
return () => clearTimeout(newTimeoutRef);
}, [scrollTop]);
Then you set the scrollTop
(as a component state) inside the event that was previously causing the scroll to top.
The hack here is that setTimeout
, which basically gives the DOM time to scroll to the top before calling your el.scrollTo
, it flashes a bit, but it was the best solution that worked for me.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Ben |
Solution 2 | thor |
Solution 3 | zx485 |
Solution 4 | Julian |
Solution 5 | Rajat Juneja |
Solution 6 | joshkarges |