Javascript React Router v4-切换组件时保持滚动位置
我用react路由器创建了两个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(),您可以猜到,它会将其滚动到视图
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,因此由于其他与路径无关的属性更改,我没有滚动窗口进行更新。为什么在上面的代码中需要设置超时?