Virtual React Example: Scroll To Item (List)
VirtualScroller.scrollToIndex method works with dynamic item sizes and performs scrolling until desired index is reached. Smooth scrolling is supported.
import { useState, memo, useEffect } from "react";
import { useVirtual, List, createListItemRef } from "@af-utils/virtual-react";
import { VirtualScroller } from "@af-utils/virtual-core";
import type { ListItemProps } from "@af-utils/virtual-react";
import type { FormEvent } from "react";
import css from "./style.module.css";
const DEFAULT_ROW_COUNT = 50000;
const Item = memo<ListItemProps>(({ model, i, data: pseudoRandomSizes }) => (
<div
ref={createListItemRef(model, i)}
className={css.item}
style={{
padding: `${pseudoRandomSizes[i]}px 0.7em`
}}
>
row {i}: {pseudoRandomSizes[i]}px
</div>
));
const getScrollSubmitHandler =
(model: VirtualScroller) => (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const idx = Number.parseInt(e.currentTarget.idx.value, 10);
if (!Number.isNaN(idx)) {
model.scrollToIndex(idx, true);
}
};
const ScrollToItem = () => {
const [pseudoRandomSizes, changeRows] = useState(() =>
Array.from(
{ length: DEFAULT_ROW_COUNT },
(_, i) => 20 + ((i ** 2) & 31)
)
);
const model = useVirtual({
itemCount: pseudoRandomSizes.length,
estimatedItemSize: 75
});
useEffect(() => {
model.scrollToIndex(pseudoRandomSizes.length - 1);
}, [model, pseudoRandomSizes.length]);
const rowsAddSubmitHandler = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const rowsToAdd = Number.parseInt(e.currentTarget.rowsToAdd.value, 10);
if (!Number.isNaN(rowsToAdd) && rowsToAdd !== 0) {
changeRows(rows =>
rowsToAdd > 0
? rows.concat(
Array.from(
{ length: rowsToAdd },
(_, i) => 50 + ((i ** 2) & 63)
)
)
: rows.slice(0, rowsToAdd)
);
} else {
model.scrollToIndex(pseudoRandomSizes.length - 1);
}
};
return (
<List
model={model}
itemData={pseudoRandomSizes}
header={
<form
className={`${css.form} ${css.top0}`}
ref={el => model.setStickyHeader(el)}
onSubmit={getScrollSubmitHandler(model)}
>
<label>
Smooth scroll to index:
<input
required
defaultValue={Math.round(
pseudoRandomSizes.length / 2
)}
name="idx"
className={css.inp}
type="number"
/>
</label>
<button className={css.btn} type="submit">
Go
</button>
</form>
}
footer={
<form
ref={el => model.setStickyFooter(el)}
className={`${css.form} ${css.bottom0}`}
onSubmit={rowsAddSubmitHandler}
>
<label>
Rows to add:
<input
defaultValue={0}
type="number"
required
name="rowsToAdd"
className={css.inp}
/>
</label>
<button className={css.btn} type="submit">
Add and scroll to end
</button>
</form>
}
>
{Item}
</List>
);
};
export default ScrollToItem;