I am using react-virtualized to prevent items in a long list being loaded when out of view port. However, I have a button at the top of the page that when clicked, scrollsTo an item that is rendered with react-virtualized.
It uses React.createRef()
to link the button with the list item but because the rendered item doesn't exist in the dom, the ref is null
(It works for the first few items that get rendered but not the rest of the items).
For example, if I click button 20, then I get the error Cannot read properties of null (reading 'getBoundingClientRect')
because the ref of the button is null because it's counterpart ref doesn't exist
Is there a way to scrollTo items that have been rendered within react-virtualized?
I have created a code sandbox here to show case what I mean.
Otherwise, here is the code snippet:
export default function App() {
const onClick = (item) => {
window.scrollTo({
top: item.ref.current.getBoundingClientRect().top,
behavior: "smooth"
});
};
const items = arr.map((el) => ({
ref: createRef(),
id: el.id
}));
return (
<div className="App">
{items.map((item) => (
<button onClick={(e) => onClick(item)}>Scroll to {item.id}</button>
))}
<WindowScroller>
{({ height, scrollTop, registerChild }) => (
<div ref={registerChild}>
<AutoSizer disableHeight>
{({ width }) => (
<>
<List
autoHeight
width={width}
height={height}
rowHeight={100}
rowRenderer={({ index }) => {
return (
<div
ref={items[index].ref}
id={`item-${items[index].id}`}
key={items[index].id}
>
{items[index].id}
</div>
);
}}
scrollTop={scrollTop}
rowCount={items.length}
overscanRowCount={100}
/>
</>
)}
</AutoSizer>
</div>
)}
</WindowScroller>
</div>
);
}
CodePudding user response:
Add a reference to your <List>
, and use that to scroll to the item's index.
const listRef = React.createRef<List>();
function scrollToItem(item) {
const index = calculateIndex(item) // figure out what index the item should have
listRef.current.scrollToItem(index) // second parameter here could be 'auto', 'smart', 'center', 'end', 'start'
}
return (
/* ... */
<List
ref={listRef}
/* ... */
/>
/* ... */
)
CodePudding user response:
Let’s use the List component to render the list in a virtualized way:
const listHeight = 600;
const rowHeight = 50;
const rowWidth = 800;
//...
<div className="list">
<List
width={rowWidth}
height={listHeight}
rowHeight={rowHeight}
rowRenderer={this.renderRow}
rowCount={this.list.length} />
</div>
Notice two things.
First, the List component requires you to specify the width and height of the list. It also needs the height of the rows so it can calculate which rows are going to be visible.
The rowHeight property takes either a fixed row height or a function that returns the height of a row given its index.
Second, the component needs the number of rows (the list length) and a function to render each row. It doesn’t take the list directly.