在渲染时将滚动反应到顶部——通常的答案似乎不起作用

Posted

技术标签:

【中文标题】在渲染时将滚动反应到顶部——通常的答案似乎不起作用【英文标题】:React scroll to top on render--the usual answer doesn't seem to be working 【发布时间】: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
    
    
    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  />
            </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
                      
                      minWidth="16px"
                      mr="6px"
                    />
                  )
                  thing.text
                </NavLinkWrapperInEntry>
              </li>
            );
          )
        </Flex>
      </Box>
      type !== 'arf' ? (
        <Button
          buttonColor="black"
          
          display= base: 'none', md: 'block' 
          onClick=handleBackClick
          mb="8px"
        >
          Back to dashboard
        </Button>
      ) : (
        <>
          <Button
            buttonColor="black"
            
            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"
      
      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(对我)。 【参考方案1】:

也许我错了,但我认为您不应将窗口滚动到顶部,而应将某些元素滚动到顶部。 也许这会有所帮助: Set textarea scroll from ReactJS state

【讨论】:

这似乎是个好主意,所以我尽力在MyPage 组件中实现它。我将 useLocationuseEffect 代码替换为 ``` const mainRef = useRef(null); useLayoutEffect(() => if(mainRef.current !== null) mainRef.current.scrollTop = 0; ); ``` 我还在组件的第一个 元素中添加了ref=mainRef。不幸的是,它没有帮助。

以上是关于在渲染时将滚动反应到顶部——通常的答案似乎不起作用的主要内容,如果未能解决你的问题,请参考以下文章

点击标签栏时将表格视图滚动到顶部

滚动到顶部按钮在 IE 中不起作用

在路线更改和新组件加载不起作用后以角度滚动到顶部

反应本机重新渲染导致视图滚动到顶部-我的键在渲染之间是不是发生变化?

自动滚动到顶部在 UITableView 中不起作用

以编程方式滚动到顶部不起作用?