【问题标题】: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
    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


【解决方案1】:

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

【讨论】:

  • 这似乎是个好主意,所以我尽力在MyPage 组件中实现它。我将 useLocationuseEffect 代码替换为 ``` const mainRef = useRef(null); useLayoutEffect(() => { if(mainRef.current !== null) { mainRef.current.scrollTop = 0; } }); ``` 我还在组件的第一个 元素中添加了ref={mainRef}。不幸的是,它没有帮助。
猜你喜欢
  • 1970-01-01
  • 2017-07-20
  • 1970-01-01
  • 2020-06-19
  • 1970-01-01
  • 1970-01-01
  • 2013-09-26
  • 1970-01-01
  • 2022-01-23
相关资源
最近更新 更多