【发布时间】:2022-01-03 02:03:43
【问题描述】:
我有一个带有左侧导航栏的 React 应用程序,然后页面在右侧呈现。当我在右侧查看页面并向下滚动然后使用导航栏转到另一个页面时,新页面将呈现与前一页相同的滚动位置。我希望新页面始终滚动到顶部。
这几天我做了很多阅读,每个人似乎都同意解决方案是使用useEffect,如下所示:
const location = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [location]);
这很有意义,而且我从调试中知道,只要 URL 发生变化,效果就会按预期运行。问题是调试似乎表明效果是在页面呈现在屏幕上之前运行的。 (我一步步调试:绘制左侧导航,运行效果,然后绘制页面。这对我来说毫无意义,因为效果是在绘制页面的组件中定义的,据我了解,@ 987654323@ 应该在页面渲染之后发生。)
这里有一些打字稿代码,希望它能帮助识别我的问题:
侧边栏:
interface Props<T extends Stuff> {
children: React.ReactNode | React.ReactNode[];
stuff: {
[key: string]: T;
};
title: string;
type: 'arf' | 'create' | 'edit';
handleBackClick: () => void;
}
const MySidebar = <T extends Stuff>({
children,
stuff,
title,
type,
handleBackClick,
}: Props<T>) => (
<Box
width="100%"
height="100%"
position="relative"
zIndex="1"
overflow="auto"
>
<Flex
as="aside"
width={['100%', '100%', '300px']}
left="0px"
top="0px"
position="fixed"
height={{ base: 'auto', md: '100%' }}
backgroundImage={[
`url(${BackgroundImage})`,
`url(${BackgroundImage})`,
'none',
]}
backgroundColor={['transparent', 'transparent', 'white']}
padding={{ base: '0px', md: '0px 20px 0px 20px' }}
flexDir="column"
zIndex={9}
overflow="auto"
>
<Box
flexGrow={1}
display="flex"
flexDir="column"
backgroundColor={['transparent', 'transparent', 'white']}
>
<Flex
textAlign="center"
my={['30px', '30px', '75px']}
justifyContent="center"
minHeight="31px"
>
<Box display={['none', 'none', 'block']}>
<Link to={PrivateRoutes.A_PRIVAE_ROUTE}>
<Image src={DesktopLogoIcon} alt="Logo" />
</Link>
</Box>
</Flex>
<Text
as="p"
mb="18px"
fontSize="xs"
textAlign={{ base: 'center', md: 'left' }}
>
<b>{title}</b>
</Text>
<Flex
flexDir={{ base: 'row', md: 'column' }}
px={{ base: '30px', md: '0px' }}
as="ul"
overflowX={['scroll', 'scroll', 'hidden']}
listStyleType="none"
>
{Object.keys(stuff).map((thingName) => {
const thing = stuff[thingName as keyof typeof stuff] as Stuff;
return (
<li key={`${thing.route}-${thing.text}`}>
<NavLinkWrapperInEntry
to={thing.route}
activeStyle={
type === 'edit'
? {
backgroundColor: 'white',
color: 'black',
}
: {}
}
>
{thing.completed && type !== 'arf' && (
<Image
src={CircleCheckIcon}
alt="Circle Check"
minWidth="16px"
mr="6px"
/>
)}
{thing.text}
</NavLinkWrapperInEntry>
</li>
);
})}
</Flex>
</Box>
{type !== 'arf' ? (
<Button
buttonColor="black"
width="100%"
display={{ base: 'none', md: 'block' }}
onClick={handleBackClick}
mb="8px"
>
Back to dashboard
</Button>
) : (
<>
<Button
buttonColor="black"
width="100%"
display={{ base: 'none', md: 'block' }}
mb="8px"
type="submit"
form="arf_form"
>
Save & Exit
</Button>
<ButtonLink
textDecor="underline"
onClick={handleBackClick}
mb="20px"
>
Back to Dashboard
</ButtonLink>
</>
)}
</Flex>
<MyPage type={type}>
{children}
</MyPage>
</Box>
);
export default MySidebar;
type MyPageProps = {
type: string;
children: React.ReactNode | React.ReactNode[];
}
const MyPage: React.FC<MyPageProps> = ({ type, children }) => {
const location = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [location]);
return (
<Box
width={['100%', '100%', 'calc(100% - 300px)']}
marginLeft={['0px', '0px', '300px']}
backgroundImage={`url(${BackgroundImage})`}
padding={{ base: '200px 0px 20px 0px', md: '100px 20px 20px 20px' }}
as='main'
display='flex'
flexDir='column'
alignItems='center'
position='relative'
backgroundRepeat='none'
backgroundSize='cover'
>
{location.pathname !== PrivateRoutes.ARF_ARF && type === 'arf' && (
<ArfArfArf />
)}
<Box maxWidth='900px' width='100%'>
{children}
</Box>
</Box>
);
}
export default MyPage;
ArfArfArf:
const ArfArfArf: React.FC = () => {
return (
<Box
backgroundColor="black"
p="15px"
color="white"
textAlign="center"
width="100%"
maxWidth="900px"
mb="28px"
>
<h2>
<b>
Some text we want displayed on almost every page
</b>
</h2>
</Box>
);
};
export default ArfArfArf;
在 MyPage 上呈现的典型子项是带有 id="arf_form" 的冗长表单。如果需要,我可以包含一个示例,但如果没有必要,我不想用代码完全压倒每个人。
我们确实使用了 react-router,我的研究表明,与其将 useEffect 直接放在我的组件中,不如通过创建 ScrollToTop 组件来提供更好的服务:
type ScrollToTopProps = {
children?: React.ReactNode | React.ReactNode[];
}
const ScrollToTop: React.FC<ScrollToTopProps> = ({ children }) => {
const location = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [location]);
return <>{children}</>
};
export default ScrollToTop;
我已经尝试将它插入到我的组件树的各个级别中,它似乎与将它放在我在代码中显示它的位置(在 MyPage 中)具有完全相同的效果——效果运行,但在页面之前视觉呈现,然后页面在上一页的滚动位置呈现。
这整件事让我很困惑,因为几乎我看过的每个地方都给出了相同的解决方案,而且无论出于何种原因,这个解决方案显然在这种情况下不起作用。我愿意接受任何建议。
【问题讨论】:
-
认为您可以创建一个 running 代码框来重现我们可以实时检查和调试的问题?
-
@DrewReese 我会试一试,但我主要是一个后端开发人员,最近把一个 React 前端丢在他的腿上,所以我真的不知道我在做什么做,TBH。
-
@DrewReese 以防万一你想知道,我还没有放弃给你一个运行的代码框。我一直在不停地工作;问题是我无法让它重现我的问题。滚动到顶部的通常修复方法在代码框中工作得很好,无论我做什么尝试破坏它。
-
别担心....如果你能得到一个,你可以像刚才一样在这里 ping(对我)。
标签: reactjs typescript react-router-dom