reactjs 使用React Router 6导航时恢复滚动位置

h6my8fg2  于 2023-03-17  发布在  React
关注(0)|答案(5)|浏览(244)

如何设置React Router 6,以便在导航和刷新浏览器窗口时恢复滚动位置?
React Router 5有一个page about scroll restoration,但是我找不到任何关于在the docs for v6中滚动的东西,所以我猜你应该用另一个包自己处理这个问题。很公平,但是我找不到任何与React Router 6兼容的东西。
react-scroll-restoration和oaf-react-router包需要v5版本(oaf-react-router确实列出了它支持v6,但是基本用法代码示例与v6不兼容,并且the related issue #210仍然是开放的)。
Gatsby和Next.js支持开箱即用的滚动恢复,但我看不出有一个 Package 整齐的软件包可以直接使用。
这个小小的demo app with server side rendered pages做了我想要的事情。当来回导航和刷新浏览器窗口时,滚动位置会恢复。
这里是the same app using React Router 6,它的滚动位置不会被保存和恢复,而是在页面之间重用,通常的解决方法是在页面被导航时滚动到顶部,但我对这种行为不感兴趣。
编辑:React Query writes that the issue with scroll restoration is that pages are refetching data,这意味着如果用于呈现页面的数据存在,滚动恢复就可以工作。我不能确认这一点,因为我的小React路由器6应用程序有这个问题,即使没有做任何数据提取。我觉得有一些小的东西,我认为我错过了,以使它工作。
咆哮:我很惊讶,这个问题的典型答案是在导航时调用window.scrollTo(0, 0)。这只解决了滚动位置在页面之间转移的问题。如果滚动位置没有恢复,用户在页面之间导航的体验就会严重恶化。我想这就是弹出窗口如此流行的部分原因,但它们也带来了一系列其他的UX问题。所以我真的不想用它们。

yh2wf1be

yh2wf1be1#

多亏了oaf-react-router中的这条评论,我才能让它与React Router 6一起工作。不过,有一些警告,所以我不认为这是一个专业Web应用程序的可行解决方案。
1.如代码注解中所述,oaf-react-router必须使用与react-router-dom相同版本的history。这就是HistoryRouter被导出为unstable_HistoryRouter的原因。此解决方案确实感觉相当不稳定。

  1. oaf-react-router在刷新网页时不恢复滚动位置,我不知道这是否容易实现,这是可以接受的。
import { createBrowserHistory } from 'history';
import { wrapHistory } from 'oaf-react-router';
import React from 'react';
import ReactDOM from 'react-dom';
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';

const history = createBrowserHistory();
wrapHistory(history);

ReactDOM.render(
  <React.StrictMode>
    <HistoryRouter history={history}>
      <App />
    </HistoryRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

Here a full working example on StackBlitz .

8fsztsew

8fsztsew2#

我知道我已经发布了一个答案,但我最终得到了一个不同的解决方案,我认为它值得一个单独的答案。
首先我必须意识到的是StackBlitz和CodeSandbox都有一些自定义的路径处理,会破坏滚动恢复。当在单独的窗口中打开演示应用时,这个问题仍然存在。如果应用在正常环境中运行,使用浏览器的后退和前进按钮在页面之间导航时,滚动位置会恢复。
这两个问题仍然存在:
1.当使用应用程序中的链接导航时,滚动位置在页面之间保持不变。从技术上讲,当使用React Router的<Link to="..."/>navigate(...)时,页面应滚动到顶部。
回顾:转到页面A,向下滚动并单击链接,然后转到页面B。如果使用浏览器的后退按钮导航回A,则滚动位置应恢复。如果使用页面B上的链接导航回A,则滚动位置应位于页面顶部。
1.刷新页面时应恢复(保持)页面的滚动位置,刷新时页面滚动到顶部。
我使用下面的代码解决了第一个问题,并接受了第二个问题仍然是一个问题。我认为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>
  );
};
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;
};
6rqinv9w

6rqinv9w3#

使用createBrowserHistory并将yscroll存储在会话存储中,可以实现这一点。请参考以下链接:https://medium.com/@knl.shivam.gupta/react-hooks-scroll-position-using-react-router-dom-v6-6cd59730b18d
这里,v6中的location.state用于将yscroll存储在浏览器历史堆栈中,从而可以使用useLayoutEffect操作显示。

aamkag61

aamkag614#

ScrollRestoration组件处理的正是这个问题。
这个组件将模拟加载器完成后浏览器在位置更改时的滚动恢复,以确保滚动位置恢复到正确的位置,即使跨域。
要使用它,只需在应用程序根组件中呈现一次:

import { ScrollRestoration } from "react-router-dom";
function App() {
    return <>
        <div>Some content...</div>
        <ScrollRestoration/>
    </>;
}

相关问题