【问题标题】:Restore scroll position when navigating with React Router 6使用 React Router 6 导航时恢复滚动位置
【发布时间】:2022-10-19 14:06:18
【问题描述】:

如何设置 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,但 basic usage code example 与 v6 不兼容,the related issue #210 仍然开放。)

Gatsby 和 Next.js 支持开箱即用的滚动恢复,但我看起来没有一个可以直接使用的整齐打包的包。

这个小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 Router 6 应用程序即使根本没有进行任何数据获取也存在问题。我觉得为了让它发挥作用,我觉得我缺少一些小东西。

咆哮:我很惊讶这个问题的典型答案是在导航时调用window.scrollTo(0, 0)。这仅解决了在页面之间传输滚动位置的问题。当滚动位置没有恢复时,页面之间导航时的用户体验会严重恶化。我想这就是弹出窗口如此受欢迎的部分原因,但它们带来了一长串其他用户体验问题,所以我真的想避免使用它们。

【问题讨论】:

  • RRDv5 文档解释说,RRD 做出的设计决定是不将此行为包含在 RRD 中,这取决于您。据我所知,您应该能够实现类似于ScrollRestoration 示例的东西。
  • RRDv6 似乎不包含 ScrollRestoration 和 RestoreScroll。
  • 它也不包含在 RRDv5 中,这就是我评论的重点。您需要在react-router-dom 外部自己实现此行为。
  • 是的。他们只是提供了如何实现这一点的一般指导方针。我想我希望有一个 npm 包来解决这个问题,这样我就可以专注于其他事情。感谢您指出了这一点。

标签: javascript reactjs react-router-dom


【解决方案1】:

感谢this comment in oaf-react-router,我能够让它与 React Router 6 一起使用。不过,有一些注意事项,所以我不认为这对于专业的 Web 应用程序来说是一个可行的解决方案。

  1. 正如this code comment 中所述,oaf-react-router 必须使用与react-router-dom 相同版本的history。这就是HistoryRouter 导出为unstable_HistoryRouter 的原因。这个解决方案确实感觉很不稳定。

  2. 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。

【讨论】:

    【解决方案2】:

    使用 createBrowserHistory 并将 yscroll 存储在会话存储中,可以实现这一点。 请参考以下链接: https://medium.com/@knl.shivam.gupta/react-hooks-scroll-position-using-react-router-dom-v6-6cd59730b18d

    在这里,v6 中的 location.state 用于将 yscroll 存储在浏览器历史堆栈中,因此可以使用 useLayoutEffect 操作显示。

    【讨论】:

    【解决方案3】:

    我知道我已经发布了一个答案,但我最终得到了一个不同的解决方案,我认为它值得一个单独的答案。

    我必须意识到的第一件事是StackBlitz and CodeSandbox both have some custom route handling that breaks scroll restoration。在单独的窗口中打开演示应用程序时,此问题仍然存在。如果应用程序在正常环境中运行,则使用浏览器的后退和前进按钮在页面之间导航时会恢复滚动位置。

    这两个问题仍然存在:

    1. 使用应用程序中的链接导航时,滚动位置在页面之间保持不变。从技术上讲,当使用 React Router 的 &lt;Link to="..."/&gt; 或 navigate(...) 时。页面应该滚动到顶部。

      回顾:转到页面 A,向下滚动并单击将您带到页面 B 的链接。如果您使用浏览器的后退按钮导航回 A,滚动位置应恢复。如果您使用页面 B 上的 A 链接导航回 A,则滚动位置应位于页面顶部。

    2. 刷新页面时应恢复(保留)页面的滚动位置。刷新时页面滚动到顶部。

      我使用下面的代码解决了第一个问题,并接受了第二个问题仍然是一个问题。我认为 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;
      };
      
      

    【讨论】:

    【解决方案4】:

    现在 react-router-dom v6.4 涵盖了这个问题 https://dev.to/tywenk/how-to-use-nested-routes-in-react-router-6-4jhd

    【讨论】:

      猜你喜欢
      • 2021-08-27
      • 2019-12-15
      • 2021-06-29
      • 1970-01-01
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-06
      • 1970-01-01
      相关资源
      最近更新 更多