gpt4 book ai didi

react-virtualized - InfiniteLoader 在loadMoreRows 完成后向上滚动时跳转

转载 作者:行者123 更新时间:2023-12-05 05:41:06 28 4
gpt4 key购买 nike

我有一个 react-virtualised InfiniteLoader 由单行组成。

我认为,主要问题是每个单元格的高度可能不同,并且必须为每个单元格加载不同的图像,因此高度不是静态的,并且会随着图像加载而变化。但即使当所有单元格的高度完全相同。

这是我当前使用 react-virtualised InfiniteLoaderGrid 的组件

/* eslint-disable no-underscore-dangle */
import React, {
FC,
LegacyRef,
useEffect,
useLayoutEffect,
useMemo,
useRef
} from "react";
import {
InfiniteLoader,
Grid,
SectionRenderedParams,
AutoSizer,
WindowScroller,
GridCellProps,
ColumnSizer,
CellMeasurerCache,
CellMeasurer,
Index,
InfiniteLoaderChildProps,
WindowScrollerChildProps,
Size,
SizedColumnProps
} from "react-virtualized";
import { CellMeasurerChildProps } from "react-virtualized/dist/es/CellMeasurer";
import PuffLoader from "react-spinners/PuffLoader";
import useMeasure from "react-use-measure";
import { ResizeObserver } from "@juggle/resize-observer";
import styled from "styled-components";

const LOADER_SIZE = 100;

const LoaderWrapper = styled.div`
width: calc(100% - ${LOADER_SIZE}px);
text-align: center;
height: ${LOADER_SIZE}px;
margin: 15px 0px;
`;

interface InfiniteGridProps {
items: any[] | undefined;
defaultHeight?: number | undefined;
loadMoreItems?: () => Promise<void>;
totalResults?: number | undefined;
overscanRowCount?: number;
renderItem: (props: any, rowIndex: number) => React.ReactNode | undefined;
preventScrollLoader?: boolean;
}

interface GridParent {
_scrollingContainer?: any;
}

interface IGridCellProps extends GridCellProps {
parent: GridCellProps["parent"] & GridParent;
}

interface InfiniteGridItemProps {
renderItem: InfiniteGridProps["renderItem"];
gridItem: any;
reCalculateGrid: (
rowIndex: IGridCellProps["rowIndex"],
columnIndex: IGridCellProps["columnIndex"],
measure: CellMeasurerChildProps["measure"]
) => void;
rowIndex: IGridCellProps["rowIndex"];
columnIndex: IGridCellProps["columnIndex"];
parent: IGridCellProps["parent"];
measure: CellMeasurerChildProps["measure"];
}

const InfiniteGridItem: React.FC<InfiniteGridItemProps> = ({
renderItem,
gridItem,
reCalculateGrid,
rowIndex,
columnIndex,
parent,
measure
}) => {
const [rowRef, { height }] = useMeasure({ polyfill: ResizeObserver });

useLayoutEffect(() => {
reCalculateGrid(
rowIndex,
columnIndex,
parent._scrollingContainer ? measure : () => {}
);
}, [
height,
columnIndex,
measure,
parent._scrollingContainer,
reCalculateGrid,
rowIndex
]);

return <div ref={rowRef}>{renderItem(gridItem, rowIndex)}</div>;
};

const InfiniteGrid: FC<InfiniteGridProps> = ({
items,
defaultHeight = 300,
loadMoreItems,
totalResults,
overscanRowCount = 10,
renderItem
}) => {
const loaderRef = useRef<InfiniteLoader | undefined>();

const cache = useMemo(
() =>
new CellMeasurerCache({
fixedWidth: true,
defaultHeight
}),
[defaultHeight]
);

const onResize = () => {
cache.clearAll();
if (loaderRef && loaderRef.current) {
loaderRef.current.resetLoadMoreRowsCache(true);
}
};

const reCalculateGrid = (
rowIndex: IGridCellProps["rowIndex"],
columnIndex: IGridCellProps["columnIndex"],
measure: CellMeasurerChildProps["measure"]
) => {
cache.clear(rowIndex, columnIndex);
measure();
};

const isRowLoaded = ({ index }: Index) => {
if (items && totalResults !== undefined) {
const isLoaded = !!items[index] || totalResults <= items.length;
return isLoaded;
}
return false;
};

const loadMoreRows = async () => {
if (loadMoreItems) await loadMoreItems();
};

const cellRenderer = (
{ rowIndex, columnIndex, style, key, parent }: IGridCellProps,
columnCount: number
) => {
const index = rowIndex * columnCount + columnIndex;
const gridItem = items?.[index];

if (!gridItem || !renderItem) return null;

return (
<CellMeasurer
key={key}
cache={cache}
parent={parent}
columnIndex={columnIndex}
rowIndex={rowIndex}
>
{({ registerChild, measure }: any) => (
<div
ref={registerChild}
style={{
...style,
overflow: "visible"
}}
key={key}
>
<InfiniteGridItem
renderItem={renderItem}
gridItem={gridItem}
reCalculateGrid={reCalculateGrid}
rowIndex={rowIndex}
columnIndex={columnIndex}
parent={parent}
measure={measure}
/>
</div>
)}
</CellMeasurer>
);
};

useEffect(() => {
cache.clearAll();
if (loaderRef && loaderRef.current) {
loaderRef.current.resetLoadMoreRowsCache(true);
}
}, [loaderRef, cache, items]);

const infiniteLoaderRender = () => (
<WindowScroller>
{({
height,
onChildScroll,
scrollTop,
registerChild
}: WindowScrollerChildProps) => (
<div ref={registerChild}>
<InfiniteLoader
isRowLoaded={isRowLoaded}
loadMoreRows={loadMoreRows}
rowCount={totalResults}
threshold={1}
ref={loaderRef as LegacyRef<InfiniteLoader> | undefined}
>
{({ onRowsRendered }: InfiniteLoaderChildProps) => (
<AutoSizer disableHeight onResize={onResize}>
{({ width }: Size) => {
const columnCount = Math.max(Math.floor(width / width), 1);
return (
<ColumnSizer width={width} columnCount={columnCount}>
{({ registerChild: rg }: SizedColumnProps) =>
loaderRef && loaderRef.current ? (
<Grid
autoHeight
width={width}
height={height}
scrollTop={scrollTop}
ref={rg}
overscanRowCount={overscanRowCount}
scrollingResetTimeInterval={0}
onScroll={onChildScroll}
columnWidth={Math.floor(width / columnCount)}
columnCount={columnCount}
rowCount={Math.ceil(
(!items ? overscanRowCount : items?.length) /
columnCount
)}
rowHeight={cache.rowHeight}
cellRenderer={(gridCellProps: GridCellProps) =>
cellRenderer(gridCellProps, columnCount)
}
onSectionRendered={({
rowStartIndex,
rowStopIndex,
columnStartIndex,
columnStopIndex
}: SectionRenderedParams) => {
const startIndex =
rowStartIndex * columnCount + columnStartIndex;
const stopIndex =
rowStopIndex * columnCount + columnStopIndex;
return onRowsRendered({ startIndex, stopIndex });
}}
/>
) : null
}
</ColumnSizer>
);
}}
</AutoSizer>
)}
</InfiniteLoader>
</div>
)}
</WindowScroller>
);

const shouldRenderLoader =
!(items && items.length === totalResults) &&
loadMoreItems &&
items &&
items.length > 0;

const renderBottom = () => {
if (shouldRenderLoader)
return (
<LoaderWrapper>
<PuffLoader color={"#000"} size={LOADER_SIZE} />
</LoaderWrapper>
);
return null;
};

return (
<>
{infiniteLoaderRender()}
{renderBottom()}
</>
);
};

export default InfiniteGrid;

this video可以看出,当您滚动到底部,然后尝试向上滚动时,它会剧烈移动。它应该只会向上移动几个像素,但比我预期的要多跳几个像素。

这是在我滚动之前 Just before scrolling

这是在我的鼠标滚轮上向上滚动几个像素后立即出现的 enter image description here

请注意 Test 752596 是如何靠近底部和滚动条的,我希望它在屏幕上稍微高一点,但在我不希望的时候似乎会出现整个其他项目它到。它大约在视频的 8 秒标记处,并且在那里似乎更加明显。

这是一个 CodeSandbox复制问题

有什么我可以做的让这更顺畅吗?

最佳答案

1。图片

删除 <img src={image} alt="test" /> 后我得到了改善.我在网络选项卡中看到,图像在上升时会重新加载。如果您查看 twitter 或 reddit 等无限滚动,上面的内容会部分卸载,但布局保持不变。这样它就不会弄乱文档高度。

也就是说,一旦你加载了图片,你应该将图片容器的大小设置为图片的大小,这样当图片被卸载时,布局在滚动位置上方保持不变。

2。 CSS

小心放置height: 500px; max-height: 500px ,这不足以固定高度,如果列表元素上有填充或边距,这将影响列表本身。示例:在图像上填充 1000 像素,将使您的列表元素更大,即使您将列表元素的高度设置为 400 像素。纯粹在 CSS 中,这可以用 overflow: hidden 稍微修复一下。 ,但这一切可能会打乱计算。

margin有点类似,有个地方放margin: 50px auto , 上面两个 div,div 的高度比你在 View 上看到的彩色矩形要大。

enter image description here

enter image description here

enter image description here

enter image description here

3。使用效果

每次向下滚动时遇到颠簸,我都会看到“拍手”被记录下来。可疑。

  useEffect(() => {
console.log("clap");
cache.clearAll();
if (loaderRef && loaderRef.current) {
loaderRef.current.resetLoadMoreRowsCache(true);
}
}, [loaderRef, cache, items]);

4。重载codesandbox注意事项

此外,对于任何使用 codesandbox 的人,请确保重新加载页面,而不仅仅是 iframe,否则您会收到如下错误:Children cannot be added or removed during a reorder operation. .

5。难以向上滚动

当我向上滚动时,有时会被稍微推回到底部。也许正在加载向上滚动条并更改文档高度的内容?

6。不必要的重新渲染

另外你应该避免使用这种模式:

const infiniteLoaderRender = () => (<span/>)

return (
{infiniteLoaderRender()}
)

简化并避免不必要的重新渲染:

const infiniteLoaderRender = <span/>

return (
{infiniteLoaderRender}
)

这似乎确实大大改善了滚动。但不确定是否修复了它。

7.难以重现您的问题

如果可以,不要用随机元素填充列表,而是制作一个固定列表版本,这样我们就可以轻松重现错误。

关于react-virtualized - InfiniteLoader 在loadMoreRows 完成后向上滚动时跳转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72321400/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com