Skip to content

Instantly share code, notes, and snippets.

@souporserious
Created January 3, 2024 20:10
Show Gist options
  • Save souporserious/0c26eca032d90e70ffe1b2231fe3440e to your computer and use it in GitHub Desktop.
Save souporserious/0c26eca032d90e70ffe1b2231fe3440e to your computer and use it in GitHub Desktop.
'use client'
import { useState, useLayoutEffect, useRef, useMemo } from 'react'
export function VirtualList({
data,
rowHeight,
itemsToShow = 4,
overscanCount = 2,
}: {
data: { text: string; id: number }[]
rowHeight: number
itemsToShow?: number
overscanCount?: number
}) {
const listRef = useRef<HTMLDivElement>(null)
const [visibleStartIndex, setVisibleStartIndex] = useState(0)
const itemStyles = useMemo(() => {
const styles: Record<string, React.CSSProperties> = {}
for (let index = 0; index < data.length; index++) {
styles[index] = {
position: 'absolute',
width: '100%',
height: `${rowHeight}px`,
top: `${index * rowHeight}px`,
textOverflow: 'ellipsis',
textWrap: 'nowrap',
overflow: 'hidden',
}
}
return styles
}, [data, rowHeight])
useLayoutEffect(() => {
const listNode = listRef.current
function handleScroll() {
if (listNode === null) return
const scrollTop = listNode.scrollTop
const startIndex = Math.max(
Math.floor(scrollTop / rowHeight) - overscanCount,
0
)
setVisibleStartIndex(startIndex)
}
listNode?.addEventListener('scroll', handleScroll, { passive: true })
return () => {
listNode?.removeEventListener('scroll', handleScroll)
}
}, [rowHeight, overscanCount, itemsToShow])
const items = []
const maxVisibleIndex = Math.min(
visibleStartIndex + itemsToShow + overscanCount * 2,
data.length
)
for (let index = visibleStartIndex; index < maxVisibleIndex; index++) {
const item = data[index]
items.push(
<div key={item.id} style={itemStyles[index]}>
{item.text}
</div>
)
}
return (
<div
ref={listRef}
style={{
height: `${itemsToShow * rowHeight}px`,
overflowY: 'auto',
}}
>
<div
style={{
height: `${data.length * rowHeight}px`,
position: 'relative',
}}
>
{items}
</div>
</div>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment