-
Notifications
You must be signed in to change notification settings - Fork 687
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
[feat] Always scroll to top on router change #2309
[feat] Always scroll to top on router change #2309
Conversation
Removes prior attempts to manage scrolling to top per route. This should be a universal behavior.
I removed I did not remove |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brendanfalkowski Thanks for contributing this, it makes sense to me.
I was concerned that the browser wouldn't be able to preserve scroll position for a history entry if we did this, but after trying your branch locally, it seems to work fine. That is, going forward or backward in browser history seems to return the page to the scroll position, which is good.
I'm inclined to say that the ScrollToTop
component should be a custom hook instead, though. Typically a component should return an element, or at least render an element under certain conditions. Here, it's only being used as a vessel for a side effect, a case in which we normally just use a custom hook.
Would this work?
const Routes = () => {
useScrollToTop()
return (
<Suspense fallback={fullPageLoadingIndicator}>
<Switch />
</Suspense>
)
}
// effect because the re-render happens before smooth scrolling starts. Some | ||
// browsers try to do this internally but it's not universal. | ||
|
||
const ScrollToTop = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we're migrated to function components this should be a peregrine hook, useScrollTop
. It could even be useScrollTopOnChange
and take a parameter like pathname
which it uses in the effect. Then any component could configure when it should change such as useScrollTopOnChange(user.isSignedIn)
, etc.
Could also have useScrollTopOnMount
that scrolls only on mount of a component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just saw Jimmy said basically the same thing a few days ago 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nice change, and should obviate a lot of repeated code throughout the app. That said, please make this a custom Peregrine hook :)
Naming of the hook is up for debate but I don't think we should hard code the variable (pathname
in your component) into the hook as other components may want to use the behavior on some different change. I would actually prefer it be a hook that accepts a variable on which to trigger the event. For example:
const Routes = () => {
const { pathname } = useLocation();
useScrollTopOnChange(pathname);
return (...)
}
As always, we'd be happy to make these changes if you are occupied with more important things :) Let us know!
Signed-off-by: sirugh <rugh@adobe.com>
|
||
const CartPage = lazy(() => import('../CartPage')); | ||
const CheckoutPage = lazy(() => import('../CheckoutPage')); | ||
const CreateAccountPage = lazy(() => import('../CreateAccountPage')); | ||
const Search = lazy(() => import('../../RootComponents/Search')); | ||
|
||
const Routes = () => { | ||
const { pathname } = useLocation(); | ||
useScrollTopOnChange(pathname); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some other examples I saw out on the web included also observing search
. I'm not sure if we want to implement that here or in the components that actually care about search such as pagination or the search/filter components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to do it here. Those components are changing search
but scrolling is a page-level concern; I don't think they should know / care / control the scrolling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: I wouldn't scroll to top when search params change. Yes, scrolling is a page-level concern, but so is changing the URL—even search
.
It's reasonable for a component that changes location
, which is global state, to have an opinion on whether the page should scroll. Also, on the web we try to minimize the number of overflowing containers we have, so it ends up being fairly common for a component to scroll the window.
Anyway, the issue I see with scroll-to-top on search
change is that we can't really disable that behavior on a case-by-case basis. We'd have to accept that it'll happen in every case where we ever change search
...and I don't have that level of confidence. In contrast, I do have that level of confidence when it comes to pathname
.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd have to accept that it'll happen in every case where we ever change search...and I don't have that level of confidence. In contrast, I do have that level of confidence when it comes to pathname.
To be specific, the Router
component can confidently control scrolling on path changes but not search changes. That said, other components such as the Category or Search pages should be able to utilize the new hook to handle scrolling to top as they do already.
// CURRENT
const Category = props => {
const { search } = useLocation();
const filtersFromSearch = getFilters(search);
useEffect(() => {
runFilterQuery()
window.scrollTop();
}, [filtersFromSearch])
}
// PROPOSED
const Category = props => {
const { search } = useLocation();
const filtersFromSearch = getFilters(search);
useScrollTopOnChange(filtersFromSearch);
useEffect(() => {
runFilterQuery()
}, [filtersFromSearch])
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved, and I'd also add search
.
|
||
const CartPage = lazy(() => import('../CartPage')); | ||
const CheckoutPage = lazy(() => import('../CheckoutPage')); | ||
const CreateAccountPage = lazy(() => import('../CreateAccountPage')); | ||
const Search = lazy(() => import('../../RootComponents/Search')); | ||
|
||
const Routes = () => { | ||
const { pathname } = useLocation(); | ||
useScrollTopOnChange(pathname); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to do it here. Those components are changing search
but scrolling is a page-level concern; I don't think they should know / care / control the scrolling.
Performance Test Results The following fails have been reported by WebpageTest. These numbers indicates a possible performance issue with the PR which requires further manual testing to validate. https://pr-2309.pwa-venia.com/venia-tops.html : LH Performance Expected 0.75 Actual 0.65 |
Description
On router changes, the page should always scroll to top.
This also removes prior attempts to manage scrolling-to-top per route. This should be a universal behavior.
Related Issue
Closes #2289.
Acceptance
Verification Stakeholders
Specification
Verification Steps
Screenshots / Screen Captures (if appropriate)
Checklist