Skip to content

Latest commit

 

History

History
156 lines (112 loc) · 7.26 KB

virtual-lists.md

File metadata and controls

156 lines (112 loc) · 7.26 KB

Virtual lists

@gsid/dnd supports drag and drop within and between virtual lists. This lets you have fantastic performance with very large data sets. As a general rule, you will want to start using a virtual list when your list size is more than 500 items.

virtual-board

Background: what are virtual lists?

A "virtual list" is the name given to a windowing performance optimisation technique where only the visible list items are rendered. See Rendering large lists with react-window by Addy Osmani for more background on virtual lists

windowing

Diagram from Creating more efficient React views with windowing by Brain Vaughn

There are drawbacks with using virtual lists. They stem from the fact that with a virtual list not all of the page content is rendered into the DOM.

  • Accessibility: screenreaders cannot read out all of the content of a page
  • Findability: native find (meta + f) will not find things that are not rendered in the DOM.

Support

@gsid/dnd is designed to work with existing virtual list solutions and does not have it's own virtual list abstraction. There is no official "virtual list" specification or implementation for the web. Different virtual list libraries achieve windowing through various techniques. So we cannot guarentee that @gsid/dnd will work with every virtual list library. We have created examples for react-window and react-virtualized which are the two most popular virtual list libraries for react.

Premade examples 🎁

Please raise a pull request if you would like to add examples for other virtualization libraries! ❤

React Virtuoso comes with automatic item measurement out of the box.

Usage

@gsid/dnd does not provide its own virtual list abstraction so there is a bit of wiring that you will need to do in order to get going with existing virtual list solutions 🛠

Enable overscanning

Virtualisation libraries often have overscanning enabled by default

Most virtual list libraries support the concept of overscanning. Overscanning is where a small about of non-visible items are rendered near the boundary of the window. When a scroll occurs the overscanned item can be immediately moved into view and does not need to be created. Overscanning generally leads to a more fluid experience.

It is required that overscanning be enabled for @gsid/dnd to work correctly. If overscanning is not enabled, rfd cannot tell if there are more items in the list when an item is in the last visual position. We require an overscanning value of one or more.

Set <Droppable /> | mode to virtual

Virtual lists behave differently to regular lists. You will need to tell rfd that your list is a virtual one.

<Droppable droppableId="droppable" mode="virtual">
  {/*...*/}
</Droppable>

Use the <Droppable /> | renderClone API

When using a virtual list the original dragging item can be unmounted during a drag if it becomes invisible. To get around this we require that you drag a clone of the dragging item. See our reparenting guide for more details.

<Droppable
  droppableId="droppable"
  mode="virtual"
  renderClone={(provided, snapshot, rubric) => (
    <div
      {...provided.draggableProps}
      {...provided.dragHandleProps}
      ref={provided.innerRef}
    >
      Item id: {items[rubric.source.index].id}
    </div>
  )}
>
  {/*...*/}
</Droppable>

Stand in for the placeholder

👋 This is only required when you have multiple connected lists. This is not required when using a single list

Usually we require consumers to put a placeholder (<Droppable /> | DroppableProvided | placeholder) into the list so that we can insert space into a list as needing during a drag.

<Droppable droppableId="droppable">
  {(provided, snapshot) => (
    <div ref={provided.innerRef} {...provided.droppableProps}>
      {/* Usually needed. But not for virtual lists! */}
      {provided.placeholder}
    </div>
  )}
</Droppable>

However, a placeholder does not make sense in the context of a virtual list as the dimensions of the list is not based on collective size of the visual items, but rather is calculated based on things like itemCount and itemSize. (eg height = itemSize * itemCount). For virtual lists, inserting our own node into it would not increase the size of the list. So we need you do insert the space for us!

A simple way to add extra space to a virtual list is to add a non-visible item to your list. It is important that this extra item is not a <Draggable />.

// This example uses the `react-window` API

const Row = ({ data, index, style }: RowProps) => {
  const item = data[index];

   // We are rendering an extra item for the placeholder
  if (!item) {
    return null;
  }

  return (
    <Draggable draggableId={item.id} index={index} key={item.id}>
      {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => (
        {/*...*/}
      )}
    </Draggable>
  );
});

function render(provided: DroppableProvided, snapshot: DroppableStateSnapshot) {
  // Add an extra item to our list to make space for a dragging item
  // Usually the DroppableProvided.placeholder does this, but that won't
  // work in a virtual list
  const itemCount: number = snapshot.isUsingPlaceholder
    ? quotes.length + 1
    : quotes.length;

  return (
    <List
      height={500}
      itemCount={itemCount}
      itemSize={110}
      width={300}
      outerRef={provided.innerRef}
      itemData={quotes}
    >
      {Row}
    </List>
  );
}

← Back to documentation