user1189352
user1189352

Reputation: 3885

Cannot get React-Beautiful-DND and React-Virtualized to work together

Guys I've been trying to get this to work for actually a couple days and I haven't been able to figure this out. I can get both libraries to work individually but I'm stuck on why it won't work when I combine the two. I think I'm connecting them wrong somehow, I just can't figure out what it is. Please help.

Tried to follow this guide here. Source code for guide here.

Here is a CodeSandBox of my issue.

Lastly here is a code snippet of the important parts from the CodeSandBox:

getRowRender({ index, style }) {
    const item = this.state.items[index];

    return (
      <Draggable draggableId={item.id} index={index} key={item.id}>
        {(provided, snapshot) => (
          <div
            ref={provided.innerRef}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
            style={getItemStyle(
              snapshot.isDragging,
              provided.draggableProps.style
            )}
          >
            {item.content}
          </div>
        )}
      </Draggable>
    );
  }


 render() {
    if (this.state.items) {      
      return (
        <DragDropContext onDragEnd={this.onDragEnd}>
          <Droppable
            droppableId="droppable"
            mode="virtual"
            renderClone={(provided, snapshot, rubric) => (
              <div
                ref={provided.innerRef}
                {...provided.draggableProps}
                {...provided.dragHandleProps}
                style={getItemStyle(
                  snapshot.isDragging,
                  provided.draggableProps.style
                )}
              >
                {this.state.items[rubric.source.index].content}
              </div>
            )}
          >
            {(provided, snapshot) => (
              <AutoSizer>
                {({ height, width }) => (
                  <List
                    {...provided.droppableProps}
                    height={height}
                    rowCount={this.state.items.length}
                    rowHeight={500}
                    width={width}
                    ref={(ref) => {
                      // react-virtualized has no way to get the list's ref that I can so
                      // So we use the `ReactDOM.findDOMNode(ref)` escape hatch to get the ref
                      if (ref) {
                        // eslint-disable-next-line react/no-find-dom-node
                        const whatHasMyLifeComeTo = ReactDOM.findDOMNode(ref);
                        if (whatHasMyLifeComeTo instanceof HTMLElement) {
                          provided.innerRef(whatHasMyLifeComeTo);
                        }
                      }
                    }}
                    style={getListStyle(snapshot.isDraggingOver)}
                    rowRenderer={this.getRowRender}
                  />
                )}
              </AutoSizer>
            )}
          </Droppable>
        </DragDropContext>
      );
    } else {
      return null;
    }
  }

Upvotes: 3

Views: 3171

Answers (3)

ozgrozer
ozgrozer

Reputation: 2042

Here's my approach to use both libraries together.

CodeSandbox demo

import ReactDOM from 'react-dom'
import { List } from 'react-virtualized'
import React, { memo, useState, useEffect } from 'react'
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'

const padding = 10

const reorder = (list, startIndex, endIndex) => {
  const result = Array.from(list)
  const [removed] = result.splice(startIndex, 1)
  result.splice(endIndex, 0, removed)
  return result
}

const ListItem = memo(props => {
  const {
    item,
    style,
    index,
    provided,
    isDragging
  } = props

  return (
    <div
      data-index={index}
      data-testid={item.id}
      ref={provided.innerRef}
      {...provided.draggableProps}
      {...provided.dragHandleProps}
      data-is-dragging={isDragging}
      style={{
        ...style,
        display: 'flex',
        padding: '0 10px',
        alignItems: 'center',
        boxSizing: 'border-box',
        marginBottom: `${padding}px`,
        ...provided.draggableProps.style,
        backgroundColor: props.isDragging ? '#ccc' : '#eee'
      }}
    >
      <div>{item.content}</div>
    </div>
  )
})

const rowRenderer = items => ({ index, style }) => {
  const item = items[index]

  if (!item) return null

  const patchedStyle = {
    ...style,
    top: style.top + padding,
    left: style.left + padding,
    height: style.height - padding,
    width: `calc(${style.width} - ${padding * 2}px)`
  }

  return (
    <Draggable
      index={index}
      key={item.id}
      draggableId={item.id}
    >
      {(provided, snapshot) => (
        <ListItem
          item={item}
          provided={provided}
          style={patchedStyle}
          isDragging={snapshot.isDragging}
        />
      )}
    </Draggable>
  )
}

const Column = memo(({ items }) => {
  return (
    <div style={{ display: 'flex' }}>
      <Droppable
        mode='virtual'
        droppableId='column'
        renderClone={(provided, snapshot, rubric) => (
          <ListItem
            provided={provided}
            isDragging={snapshot.isDragging}
            item={items[rubric.source.index]}
          />
        )}
      >
        {(droppableProvided, snapshot) => {
          const itemCount = snapshot.isUsingPlaceholder
            ? items.length + 1
            : items.length

          return (
            <List
              width={300}
              height={600}
              rowHeight={50}
              rowCount={itemCount}
              ref={ref => {
                if (ref) {
                  // eslint-disable-next-line react/no-find-dom-node
                  const whatHasMyLifeComeTo = ReactDOM.findDOMNode(ref)
                  if (whatHasMyLifeComeTo instanceof window.HTMLElement) {
                    droppableProvided.innerRef(whatHasMyLifeComeTo)
                  }
                }
              }}
              style={{
                transition: 'background-color 0.2s ease',
                backgroundColor: snapshot.isDraggingOver ? '#0a84e3' : '#74b9ff'
              }}
              rowRenderer={rowRenderer(items)}
            />
          )
        }}
      </Droppable>
    </div>
  )
})

export default () => {
  const [items, setItems] = useState([])

  useEffect(() => {
    const generateItems = new Array(2500).fill().map((value, id) => (({
      id: `id-${id}`,
      content: `content ${id}`
    })))
    setItems(generateItems)
  }, [])

  const onDragEnd = result => {
    if (!result.destination) return

    setItems(reorder(
      items,
      result.source.index,
      result.destination.index
    ))
  }

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Column items={items} />
    </DragDropContext>
  )
}

Upvotes: 0

Abhishek
Abhishek

Reputation: 1332

if anyone still looking for an active sandbox with working prototype. Here is the post where I have answered and added the sandbox link

react-beautiful-dnd doesn’t work properly with react-virtualized Table component

Upvotes: 0

Person
Person

Reputation: 2269

I think I managed to solve this.

https://codesandbox.io/s/vertical-list-forked-risye

You did not pass styles property in your rowRenderer to virtualize handle your rows in the right way.

Also you Autosizer was not growing past height: 0 because it's parent did not have any styles.

Upvotes: 2

Related Questions