最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

material ui - Infinite feed scroll jumps when setting overflow to scroll - Stack Overflow

programmeradmin0浏览0评论

I am using react-query for infinite loading feed and '@tanstack/react-virtual' for virtualization of the cards which are composed of mui elements. I want to keep my app height to 100vh so there's no whole screen scroll down. But the component which contains the feed can be scrollable. I use overflowY: 'scroll' on it so I can scroll inside this element without scrolling down the screen.

The Problem is that when I apply overflowY: 'scroll' to the div inside LogCards which wraps the map iterations the scroll becomes very jittery, it flickers very fast as if it has trouble calculating scroll position properly and then it breaks and all cards disappear. How can I fix this? As soon as I remove overflowY: 'scroll' and allow the whole screen to be scrolled the issue goes away. A short video of it.

Here's the component holding the feed:

const LogCards = ({ pageLimit }: LogCardsProps) => {
  const {
    data: logs,
    fetchNextPage,
    isFetchingNextPage,
    isLoading,
  } = useInfiniteLogsQuery(pageLimit);
  const { ref, inView } = useInView();

  const allLogs = useMemo(() => logs?.pages.flatMap((log) => log.results), [logs]) || [];
  const parentRef = useRef<HTMLDivElement>(null);

  const rowVirtualizer = useVirtualizer({
    count: allLogs.length,
    getScrollElement: useCallback(() => parentRef.current, []),
    estimateSize: useCallback(() => 300, []),
    overscan: 2,
    gap: 8,
  });

  useEffect(() => {
    if (inView) fetchNextPage();
  }, [fetchNextPage, inView]);

  return !isLoading ? (
    <Box ref={parentRef} sx={{ overflowY: 'scroll' }}>
      <div style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
        {allLogs &&
          rowVirtualizer.getVirtualItems().map((log, index) => {
            const LogCardData = allLogs[log.index];
            console.log(LogCardData);

            if (!LogCardData) return <span key={`no card index ${index}`}>No cards</span>;
            return (
              <LogCard
                key={LogCardData.id}
                data-index={log.index}
                ref={rowVirtualizer.measureElement}
                log={LogCardData}
              />
            );
          })}
      </div>
      <Box sx={{ height: '20px' }} ref={ref}>
        {isFetchingNextPage && <LinearProgress sx={{ m: '2px' }} />}
      </Box>
    </Box>
  ) : (
    <LinearLoader />
  );
};

Here's the component which holds LogCards:

const LogsList = () => {
  const [pageLimit, setPageLimit] = useState(25);

  return (
    <Paper
      sx={{
        display: 'flex',
        flexDirection: 'column',
        height: '100vh',
        px: 1,
        borderRadius: { xs: '4px', md: 0 },
      }}
    >
      <Typography variant="h5" textAlign="center" sx={{ py: 2 }}>
        List of unsolved logs
      </Typography>
      <LogsLimitButtons pageLimit={pageLimit} setPageLimit={setPageLimit} />
      <LogCards pageLimit={pageLimit} />
    </Paper>
  );
};

And here's general layout of the app:

const LaunchDesktop = () => {
  return (
    <Grid2 container spacing={1}>
      <Grid2
        size={{ xs: 12, md: 8 }}
        sx={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        <SosForm />
      </Grid2>
      <Grid2 size={{ xs: 12, md: 4 }}>
        <LogsList />
      </Grid2>
    </Grid2>
  );
};

I am using react-query for infinite loading feed and '@tanstack/react-virtual' for virtualization of the cards which are composed of mui elements. I want to keep my app height to 100vh so there's no whole screen scroll down. But the component which contains the feed can be scrollable. I use overflowY: 'scroll' on it so I can scroll inside this element without scrolling down the screen.

The Problem is that when I apply overflowY: 'scroll' to the div inside LogCards which wraps the map iterations the scroll becomes very jittery, it flickers very fast as if it has trouble calculating scroll position properly and then it breaks and all cards disappear. How can I fix this? As soon as I remove overflowY: 'scroll' and allow the whole screen to be scrolled the issue goes away. A short video of it.

Here's the component holding the feed:

const LogCards = ({ pageLimit }: LogCardsProps) => {
  const {
    data: logs,
    fetchNextPage,
    isFetchingNextPage,
    isLoading,
  } = useInfiniteLogsQuery(pageLimit);
  const { ref, inView } = useInView();

  const allLogs = useMemo(() => logs?.pages.flatMap((log) => log.results), [logs]) || [];
  const parentRef = useRef<HTMLDivElement>(null);

  const rowVirtualizer = useVirtualizer({
    count: allLogs.length,
    getScrollElement: useCallback(() => parentRef.current, []),
    estimateSize: useCallback(() => 300, []),
    overscan: 2,
    gap: 8,
  });

  useEffect(() => {
    if (inView) fetchNextPage();
  }, [fetchNextPage, inView]);

  return !isLoading ? (
    <Box ref={parentRef} sx={{ overflowY: 'scroll' }}>
      <div style={{ height: `${rowVirtualizer.getTotalSize()}px` }}>
        {allLogs &&
          rowVirtualizer.getVirtualItems().map((log, index) => {
            const LogCardData = allLogs[log.index];
            console.log(LogCardData);

            if (!LogCardData) return <span key={`no card index ${index}`}>No cards</span>;
            return (
              <LogCard
                key={LogCardData.id}
                data-index={log.index}
                ref={rowVirtualizer.measureElement}
                log={LogCardData}
              />
            );
          })}
      </div>
      <Box sx={{ height: '20px' }} ref={ref}>
        {isFetchingNextPage && <LinearProgress sx={{ m: '2px' }} />}
      </Box>
    </Box>
  ) : (
    <LinearLoader />
  );
};

Here's the component which holds LogCards:

const LogsList = () => {
  const [pageLimit, setPageLimit] = useState(25);

  return (
    <Paper
      sx={{
        display: 'flex',
        flexDirection: 'column',
        height: '100vh',
        px: 1,
        borderRadius: { xs: '4px', md: 0 },
      }}
    >
      <Typography variant="h5" textAlign="center" sx={{ py: 2 }}>
        List of unsolved logs
      </Typography>
      <LogsLimitButtons pageLimit={pageLimit} setPageLimit={setPageLimit} />
      <LogCards pageLimit={pageLimit} />
    </Paper>
  );
};

And here's general layout of the app:

const LaunchDesktop = () => {
  return (
    <Grid2 container spacing={1}>
      <Grid2
        size={{ xs: 12, md: 8 }}
        sx={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        <SosForm />
      </Grid2>
      <Grid2 size={{ xs: 12, md: 4 }}>
        <LogsList />
      </Grid2>
    </Grid2>
  );
};
Share Improve this question edited Feb 6 at 12:16 magrega asked Feb 6 at 11:17 magregamagrega 16810 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Refered to this example on tanstack website and wrapped my LogCard iteration with a div that has a transform style. I guess under the hood all of the elements are absolutely positined and need to be explicitly set.

const LogCards = ({ pageLimit }: LogCardsProps) => {
  const {
    data: logs,
    fetchNextPage,
    isFetchingNextPage,
    isLoading,
  } = useInfiniteLogsQuery(pageLimit);
  const { ref, inView } = useInView();

  const allLogs = useMemo(() => logs?.pages.flatMap((log) => log.results), [logs]) || [];
  const parentRef = useRef<HTMLDivElement>(null);

  const rowVirtualizer = useVirtualizer({
    count: allLogs.length,
    getScrollElement: useCallback(() => parentRef.current, []),
    estimateSize: useCallback(() => 300, []),
    overscan: 2,
    gap: 8,
  });

  useEffect(() => {
    if (inView) fetchNextPage();
  }, [fetchNextPage, inView]);

  return !isLoading ? (
    <Box
      ref={parentRef}
      sx={{
        overflowY: 'scroll',
      }}
    >
      <div
        style={{
          height: rowVirtualizer.getTotalSize(),
          position: 'relative',
        }}
      >
        <div
          style={{
            transform: `translateY(${rowVirtualizer.getVirtualItems()[0]?.start ?? 0}px)`,
          }}
        >
          {allLogs &&
            rowVirtualizer.getVirtualItems().map((log, index) => {
              const LogCardData = allLogs[log.index];
              if (!LogCardData) return <span key={`no card index ${index}`}>No cards</span>;
              return (
                <LogCard
                  key={LogCardData.id}
                  data-index={log.index}
                  ref={rowVirtualizer.measureElement}
                  log={LogCardData}
                />
              );
            })}
        </div>
      </div>
      <Box sx={{ height: '20px' }} ref={ref}>
        {isFetchingNextPage && <LinearProgress sx={{ m: '2px' }} />}
      </Box>
    </Box>
  ) : (
    <LinearLoader />
  );
};
发布评论

评论列表(0)

  1. 暂无评论