Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript React Router v4-切换组件时保持滚动位置_Javascript_Reactjs_Scrollview_React Router V4_React Router Dom - Fatal编程技术网

Javascript React Router v4-切换组件时保持滚动位置

Javascript React Router v4-切换组件时保持滚动位置,javascript,reactjs,scrollview,react-router-v4,react-router-dom,Javascript,Reactjs,Scrollview,React Router V4,React Router Dom,我用react路由器创建了两个s /卡片->游戏卡片列表 /纸牌/1->纸牌游戏详情#1 当用户单击“返回列表”时,我想滚动用户在列表中的位置 我如何才能做到这一点?在 React Router v4不提供对滚动恢复的开箱即用支持,目前它们也不提供。在他们的文档中,你可以阅读更多关于它的内容 因此,每个开发人员都需要编写逻辑来支持这一点,尽管我们确实有一些工具来实现这一点 元素。scrollIntoView() 可以对元素调用.scrollIntoView(),您可以猜到,它会将其滚动到视图

我用react路由器创建了两个
s

  • /卡片->游戏卡片列表
  • /纸牌/1->纸牌游戏详情#1
当用户单击“返回列表”时,我想滚动用户在列表中的位置


我如何才能做到这一点?

React Router v4不提供对滚动恢复的开箱即用支持,目前它们也不提供。在他们的文档中,你可以阅读更多关于它的内容

因此,每个开发人员都需要编写逻辑来支持这一点,尽管我们确实有一些工具来实现这一点

元素。scrollIntoView() 可以对元素调用
.scrollIntoView()
,您可以猜到,它会将其滚动到视图中。支持非常好,目前97%的浏览器支持它

组件可以传递状态 React路由器的链接组件有一个
to
属性,您可以提供一个对象而不是字符串。这就是这个人

<Link to={{ pathname: '/card', state: 9 }}>Card nine</Link>

另一种可能的解决方案是将您的
/cards/:id
路由呈现为全屏模式,并将
/cards
路由安装在其后面

以便使用Redux进行全面实施,您可以在上看到这一点

我通过使用历史API实现了这一点

  • 更改路线后保存滚动位置

  • 当用户单击后退按钮时,恢复滚动位置

  • getSnapshotBeforeUpdate
    中保存滚动位置,并在
    componentDidUpdate
    中恢复滚动位置

      // Saving scroll position.
      getSnapshotBeforeUpdate(prevProps) {
        const {
          history: { action },
          location: { pathname }
        } = prevProps;
    
        if (action !== "POP") {
          scrollData = { ...scrollData, [pathname]: window.pageYOffset };
        }
    
        return null;
      }
    
      // Restore scroll position.
      componentDidUpdate() {
        const {
          history: { action },
          location: { pathname }
        } = this.props;
    
        if (action === "POP") {
          if (scrollData[pathname]) {
            setTimeout(() =>
              window.scrollTo({
                left: 0,
                top: scrollData[pathname],
                behavior: "smooth"
              })
            );
          } else {
            setTimeout(window.scrollTo({ left: 0, top: 0 }));
          }
        } else {
          setTimeout(window.scrollTo({ left: 0, top: 0 }));
        }
      }
    

    由于没有关于如何在功能组件中实现这一点的答案,因此我为一个项目实现了一个钩子解决方案:

    import React from 'react';
    import { useHistory } from 'react-router-dom';
    
    function useScrollMemory(): void {
      const history = useHistory<{ scroll: number } | undefined>();
    
      React.useEffect(() => {
        const { push, replace } = history;
    
        // Override the history PUSH method to automatically set scroll state.
        history.push = (path: string) => {
          push(path, { scroll: window.scrollY });
        };
        // Override the history REPLACE method to automatically set scroll state.
        history.replace = (path: string) => {
          replace(path, { scroll: window.scrollY });
        };
    
        // Listen for location changes and set the scroll position accordingly.
        const unregister = history.listen((location, action) => {
          window.scrollTo(0, action !== 'POP' ? 0 : location.state?.scroll ?? 0);
        });
    
        // Unregister listener when component unmounts.
        return () => {
          unregister();
        };
      }, [history]);
    }
    
    function App(): JSX.Element {
      useScrollMemory();
    
      return <div>My app</div>;
    }
    
    从“React”导入React;
    从'react router dom'导入{useHistory};
    函数useCrollMemory():void{
    const history=useHistory();
    React.useffect(()=>{
    const{push,replace}=历史;
    //覆盖历史推送方法以自动设置滚动状态。
    history.push=(路径:字符串)=>{
    推送(路径,{scroll:window.scrollY});
    };
    //重写历史记录替换方法以自动设置滚动状态。
    history.replace=(路径:字符串)=>{
    替换(路径,{scroll:window.scrollY});
    };
    //收听位置更改并相应地设置滚动位置。
    const unregister=history.listen((位置、操作)=>{
    window.scrollTo(0,action!=“POP”?0:位置.状态?.scroll?0);
    });
    //组件卸载时注销侦听器。
    return()=>{
    注销();
    };
    },[历史];
    }
    函数App():JSX.Element{
    使用滚动内存();
    返回我的应用程序;
    }
    
    使用此覆盖解决方案,您无需担心在所有
    链接中传递状态。一个改进是使它具有通用性,这样它就可以向后兼容
    history
    push
    replace
    方法,但这在我的特殊情况下不是必需的,所以我省略了它


    我使用的是
    react router dom
    ,但您也可以很容易地覆盖vanilla historyAPI的方法。

    这回答了问题,但在用户单击浏览器后退按钮时并没有解决问题。此解决方案对我来说很有效,只使用了几个较小的MOD。我没有更新getSnapshotBeforeUpdate中的scrollData对象,而是将一个取消公告的事件处理程序绑定到“scroll”事件,这样我就不会频繁地更新scrollData。我还修改了componentDidUpdate函数,以便仅在pathname!==prevProp.location.pathname,因此由于其他与路径无关的属性更改,我没有滚动窗口进行更新。为什么在上面的代码中需要设置超时?