使用 reactjs 的虚拟化列表
virtualized list with reactjs
最近我遇到了 reat-window 图书馆,它很棒而且很方便。这让我思考它是如何构建的,所以我决定尝试一下并构建我自己的虚拟化列表
但是列表只能滚动到项目“60”。想知道我的索引计算有什么问题,看看 react-window 的代码,好像我们在做类似的事情 src1 src2。
知道我的指数计算有什么问题吗
import React, { useMemo, useCallback, useState } from 'react';
const ListItem = ({ val, height }) => {
return <li style={{ height: `${height}px` }}>{val}</li>;
};
const ITEM_HEIGHT = 20;
const CONTAINER_HEIGHT = 300;
const BUFFER = Math.ceil(CONTAINER_HEIGHT / ITEM_HEIGHT);
export const App = () => {
const [scrollTop, setScrollTop] = useState(0);
const allItems = useMemo(() => {
const items = [];
for (let i = 0; i < 500; i++) {
items.push(<ListItem val={i} height={ITEM_HEIGHT} />)
}
return items;
}, [ITEM_HEIGHT]);
const onScrollHandler = useCallback((event) => {
setScrollTop(event.currentTarget.scrollTop);
}, [allItems, setScrollTop]);
// start index = (total scrolled pixel / row height) - buffer
const start = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER);
// end index = (total scrolled pixel + list height) / row height + buffer
const end = Math.min(allItems.length - 1, Math.ceil((scrollTop + CONTAINER_HEIGHT) / ITEM_HEIGHT) + BUFFER);
const itemsToBeRender = allItems.slice(start, end);
return (
<div
style={{ overflowY: "scroll" }}
onScroll={onScrollHandler}>
<ul
style={{ height: `${CONTAINER_HEIGHT}px` }}
>
{itemsToBeRender}
</ul>
</div>
);
};
我做了一些 google 搜索并找到了 this article。添加 "position" 属性后,一切正常。
import React, { useMemo, useCallback, useState } from 'react';
const ITEM_HEIGHT = 20;
const CONTAINER_HEIGHT = 300;
const BUFFER = Math.ceil(CONTAINER_HEIGHT / ITEM_HEIGHT);
export const App = () => {
const [scrollTop, setScrollTop] = useState(0);
const allItems = useMemo(() => {
const items = [];
for (let i = 0; i < 200; i++) {
const style = {
height: `${ITEM_HEIGHT}px`,
position: "absolute",
top: `${i * ITEM_HEIGHT}px`,
};
items.push(<li key={i} style={style} >{i}</li>)
}
return items;
}, [ITEM_HEIGHT]);
const onScrollHandler = useCallback((event) => {
setScrollTop(event.currentTarget.scrollTop);
}, [allItems, setScrollTop]);
// fake we have a lots of item, so that the scroll bar will always show up
const fullHeight = allItems.length * ITEM_HEIGHT;
const start = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER);
const end = Math.min(
allItems.length - 1,
Math.floor((scrollTop + CONTAINER_HEIGHT) / ITEM_HEIGHT) + BUFFER);
const itemsToBeRender = allItems.slice(start, end + 1);
return (
<div
style={{ overflowY: "scroll", height: `${CONTAINER_HEIGHT}px` }}
onScroll={onScrollHandler}>
<ul
style={{ height: `${fullHeight}px`, position: "relative" }}
>
{itemsToBeRender}
</ul>
</div>
);
};
最近我遇到了 reat-window 图书馆,它很棒而且很方便。这让我思考它是如何构建的,所以我决定尝试一下并构建我自己的虚拟化列表
但是列表只能滚动到项目“60”。想知道我的索引计算有什么问题,看看 react-window 的代码,好像我们在做类似的事情 src1 src2。
知道我的指数计算有什么问题吗
import React, { useMemo, useCallback, useState } from 'react';
const ListItem = ({ val, height }) => {
return <li style={{ height: `${height}px` }}>{val}</li>;
};
const ITEM_HEIGHT = 20;
const CONTAINER_HEIGHT = 300;
const BUFFER = Math.ceil(CONTAINER_HEIGHT / ITEM_HEIGHT);
export const App = () => {
const [scrollTop, setScrollTop] = useState(0);
const allItems = useMemo(() => {
const items = [];
for (let i = 0; i < 500; i++) {
items.push(<ListItem val={i} height={ITEM_HEIGHT} />)
}
return items;
}, [ITEM_HEIGHT]);
const onScrollHandler = useCallback((event) => {
setScrollTop(event.currentTarget.scrollTop);
}, [allItems, setScrollTop]);
// start index = (total scrolled pixel / row height) - buffer
const start = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER);
// end index = (total scrolled pixel + list height) / row height + buffer
const end = Math.min(allItems.length - 1, Math.ceil((scrollTop + CONTAINER_HEIGHT) / ITEM_HEIGHT) + BUFFER);
const itemsToBeRender = allItems.slice(start, end);
return (
<div
style={{ overflowY: "scroll" }}
onScroll={onScrollHandler}>
<ul
style={{ height: `${CONTAINER_HEIGHT}px` }}
>
{itemsToBeRender}
</ul>
</div>
);
};
我做了一些 google 搜索并找到了 this article。添加 "position" 属性后,一切正常。
import React, { useMemo, useCallback, useState } from 'react';
const ITEM_HEIGHT = 20;
const CONTAINER_HEIGHT = 300;
const BUFFER = Math.ceil(CONTAINER_HEIGHT / ITEM_HEIGHT);
export const App = () => {
const [scrollTop, setScrollTop] = useState(0);
const allItems = useMemo(() => {
const items = [];
for (let i = 0; i < 200; i++) {
const style = {
height: `${ITEM_HEIGHT}px`,
position: "absolute",
top: `${i * ITEM_HEIGHT}px`,
};
items.push(<li key={i} style={style} >{i}</li>)
}
return items;
}, [ITEM_HEIGHT]);
const onScrollHandler = useCallback((event) => {
setScrollTop(event.currentTarget.scrollTop);
}, [allItems, setScrollTop]);
// fake we have a lots of item, so that the scroll bar will always show up
const fullHeight = allItems.length * ITEM_HEIGHT;
const start = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER);
const end = Math.min(
allItems.length - 1,
Math.floor((scrollTop + CONTAINER_HEIGHT) / ITEM_HEIGHT) + BUFFER);
const itemsToBeRender = allItems.slice(start, end + 1);
return (
<div
style={{ overflowY: "scroll", height: `${CONTAINER_HEIGHT}px` }}
onScroll={onScrollHandler}>
<ul
style={{ height: `${fullHeight}px`, position: "relative" }}
>
{itemsToBeRender}
</ul>
</div>
);
};