React Beautiful DND
The example below integrates React Virtuoso with the maintained fork of React Beautiful DND, hello-pangea/dnd. The example works with Beautiful DND too, but causes warnings with StrictMode.
Live Editor
// Generate our initial big data set const initial = Array.from({ length: 1000 }, (_, k) => ({ id: `id:${k}`, text: `item ${k}` })); function reorder(list, startIndex, endIndex) { const result = Array.from(list); const [removed] = result.splice(startIndex, 1); result.splice(endIndex, 0, removed); return result; } function Item({ provided, item, isDragging }) { return ( <div style={{ paddingBottom: "8px" }}> <div {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} style={provided.draggableProps.style} className={`item ${isDragging ? "is-dragging" : ""}`} > {item.text} </div> </div> ); } function App() { const [items, setItems] = useState(initial) const onDragEnd = React.useCallback( (result) => { if (!result.destination) { return } if (result.source.index === result.destination.index) { return } // void setItems setItems((items) => reorder(items, result.source.index, result.destination.index)) }, [setItems] ) const HeightPreservingItem = React.useCallback(({ children, ...props }) => { const [size, setSize] = useState(0); const knownSize = props["data-known-size"]; useEffect(() => { setSize((prevSize) => { return knownSize == 0 ? prevSize : knownSize; }); }, [knownSize]); return ( <div {...props} className="height-preserving-container" style={{ "--child-height": `${size}px` }} > {children} </div> ); }, []); return ( <div style={{ padding: '1rem' }}> <style> {` .height-preserving-container:empty { min-height: calc(var(--child-height)); box-sizing: border-box; } `} </style> <PangeaDND.DragDropContext onDragEnd={onDragEnd}> <PangeaDND.Droppable droppableId="droppable" mode="virtual" renderClone={(provided, snapshot, rubric) => ( <Item provided={provided} isDragging={snapshot.isDragging} item={items[rubric.source.index]} /> )} > {(provided) => { return ( <Virtuoso useWindowScroll components={{ Item: HeightPreservingItem }} scrollerRef={provided.innerRef} data={items} style={{ width: 300, height: 500 }} itemContent={(index, item) => { return ( <PangeaDND.Draggable draggableId={item.id} index={index} key={item.id} > {(provided) => ( <Item provided={provided} item={item} isDragging={false} />)} </PangeaDND.Draggable> ); }} /> ); }} </PangeaDND.Droppable> </PangeaDND.DragDropContext> </div> ); } render(<App />)
Result
Loading...