Jan*_*ard 13 javascript reactjs react-router-dom
如何设置 React Router 6 以在导航和刷新浏览器窗口时恢复滚动位置?
React Router 5 有一个关于滚动恢复的页面,但我在v6 的文档中找不到任何有关滚动的内容,所以我猜你应该用另一个包自己处理这个问题。很公平,但我找不到任何与 React Router 6 兼容的东西。
包react-scroll-restoration和oaf-react-router需要v5。(oaf-react-router 确实列出了它支持 v6,但基本使用代码示例与 v6 不兼容,相关问题 #210仍然处于开放状态。)
Gatsby 和 Next.js 支持开箱即用的滚动恢复,但我看起来没有一个可以直接使用的整齐打包的包。
这个带有服务器端渲染页面的小演示应用程序可以满足我的需求。当来回导航并刷新浏览器窗口时,滚动位置会恢复。
这是使用 React Router 6 的同一个应用程序,其中滚动位置不会保存和恢复,但实际上在页面之间重用。通常的解决方法是每当导航页面时滚动到顶部,但我对这种行为不感兴趣。
编辑:React Query 写道,滚动恢复的问题是页面正在重新获取数据,这意味着如果用于渲染页面的数据存在,则滚动恢复就可以工作。我无法确认这一点,因为我的小型 React Router 6 应用程序即使根本没有进行任何数据获取也存在问题。我觉得为了让它发挥作用,我缺少一些小东西。
Rant:令我感到非常惊讶的是,这个问题的典型答案是在导航时打电话window.scrollTo(0, 0)
。这仅解决了页面之间滚动位置转移的问题。当滚动位置未恢复时,在页面之间导航时的用户体验会严重恶化。我想这就是弹出窗口变得如此流行的部分原因,但它们带来了一系列其他用户体验问题,所以我真的想避免使用它们。
该ScrollRestoration
组件正是处理这个问题的。它需要使用数据路由器,例如通过调用创建的数据路由器createBrowserRouter
(建议所有新的 React Router Web 项目使用)。
该组件将在加载程序完成后模拟浏览器在位置更改时的滚动恢复,以确保滚动位置恢复到正确的位置,即使跨域也是如此。
要使用它,只需在应用程序根组件中渲染一次即可:
import { ScrollRestoration } from "react-router-dom";
function App() {
return <>
<div>Some content...</div>
<ScrollRestoration/>
</>;
}
Run Code Online (Sandbox Code Playgroud)
我知道我已经发布了一个答案,但我最终得到了一个不同的解决方案,我认为它值得一个单独的答案。
我必须意识到的第一件事是StackBlitz 和 CodeSandbox 都有一些会破坏滚动恢复的自定义路由处理。在单独的窗口中打开演示应用程序时,此问题仍然存在。如果应用程序在正常环境中运行,则使用浏览器的后退和前进按钮在页面之间导航时会恢复滚动位置。
这两个问题仍然存在:
使用应用程序中的链接导航时,滚动位置在页面之间保持不变。从技术上讲,当使用 React Router<Link to="..."/>
或navigate(...)
. 页面应该滚动到顶部。
回顾:转到页面 A,向下滚动并单击链接,将您带到页面 B。如果您使用浏览器的后退按钮导航回 A,则应该恢复滚动位置。如果您使用页面 B 上指向 A 的链接导航回 A,则滚动位置应位于页面顶部。
刷新页面时应恢复(保留)页面的滚动位置。刷新时页面会滚动到顶部。
我使用下面的代码解决了第一个问题,并接受第二个问题仍然是一个问题。我认为 Gatsby 和 Next.js 能够通过将滚动位置存储在会话存储中来解决刷新问题,这对于我的用例来说感觉有点大材小用。
import { MouseEventHandler, ReactNode } from "react";
import { Link } from "react-router-dom";
import { useNavigateToTop } from "./useNavigateToTop";
interface Props {
children: ReactNode;
className?: string;
to: string;
}
/** Link to the top of a page so that the scroll position isn't persisted between pages. Use this instead of React's build-in @see {@link Link}. */
export const LinkToTop = (props: Props) => {
const navigateToTop = useNavigateToTop();
const navigateAndReset: MouseEventHandler<HTMLAnchorElement> = (event) => {
event.preventDefault();
navigateToTop(props.to);
};
return (
<Link className={props.className} onClick={navigateAndReset} to={props.to}>
{props.children}
</Link>
);
};
Run Code Online (Sandbox Code Playgroud)
import { useNavigate } from "react-router-dom";
/** Navigate to the top of a page so that the scroll position isn't persisted between pages. Use this instead of React Dom's build-in @see {@link useNavigate}. */
export const useNavigateToTop = () => {
const navigate = useNavigate();
const navigateAndReset = (to: string) => {
navigate(to, { replace: true });
window.scrollTo(0, 0);
};
return navigateAndReset;
};
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
23020 次 |
最近记录: |