Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion: Virtual list api #1225

Closed
alexreardon opened this issue Apr 7, 2019 · 48 comments
Closed

Discussion: Virtual list api #1225

alexreardon opened this issue Apr 7, 2019 · 48 comments

Comments

@alexreardon
Copy link
Collaborator

alexreardon commented Apr 7, 2019

We are planning on shipping virtual list support #68. However, I am not sure what the api should look like.

πŸ‘¨β€πŸ« Full disclosure: I am not a virtual list expert, so please feel free to correct any inaccuracies in my understanding

Let's get into it

One big difference from an API perspective of supporting virtual lists is that the dragging item (<Draggable />) can be removed during a drag. Currently, this is not the case. So in order to support virtual lists we need to support the ability to render a clone as the dragging item.

import React from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from "react-window";
import { Draggable, VirtualDroppable } from "react-beautiful-dnd";

// Note: this example won't work - it is just to see what an api could look like

const Row = ({ index, style }) => (
  <Draggable draggableId={index}>
    {provided => (
      <div
        {...provided.dragHandleProps}
        {...provided.draggableProps}
        style={{
          ...style,
          ...provided.draggableProps.style
        }}
        ref={provided.innerRef}
      >
        Row {index}
      </div>
    )}
  </Draggable>
);

const Example = () => (
  <VirtualDroppable
    droppableId="droppable"
    renderDraggingItem={provided => (
      <div
        {...provided.dragHandleProps}
        {...provided.draggableProps}
        ref={provided.innerRef}
      >
        Drag Me
      </div>
    )}
  >
    {droppableProvided => (
      <List
        height={150}
        itemCount={1000}
        itemSize={35}
        width={300}
        innerRef={droppableProvided.innerRef}
      >
        {Row}
        {/* no placeholder needed */}
      </List>
    )}
  </VirtualDroppable>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<Example />, rootElement);

Droppable renders a clone?

Right now I am thinking of having a renderDraggingItem prop on the Droppable (or VirtualDroppable) which would be responsible for rendering the clone.

Then I think, should we always render the dragging item as a clone? I think not as things are working quite well right now, but it is something to consider.

Persistent?

We could get away from needing a clone if we could mark an item in a virtual list as persistent after a drag starts so the react component is never unmounted.

Droppable placeholder

This would not be needed for a home list as the full list size would not collapse when an item is removed. A foreign list might need an insertion to make room... Oh dear

@theKashey
Copy link

Edge case - virtual table. A "row" is a <tr><td>..</td></tr> and has to be rendered inside a parent table which could be create by a VirtualTable itself.

So in short - some times you have to render a clone inside a virtual List, and some Lists gives you the ability to "inject" something inside, that changes how renderDraggingItem works - it shall be just passed down to render props.

 <VirtualDroppable
    droppableId="droppable"
    renderDraggingItem={provided => (
      <div
        {...provided.dragHandleProps}
        {...provided.draggableProps}
        ref={provided.innerRef}
      >
        Drag Me
      </div>
    )}
  >
    {({droppableProvided, VirtalDraggingItem} => (
      <List
        height={150}
        itemCount={1000}
        itemSize={35}
        width={300}
        innerRef={droppableProvided.innerRef}
        
        extraChildren={VirtalDraggingItem} // not a real API
      >
        {Row}
      </List>
    )}
  </VirtualDroppable>
);

That would be more or less any VirtualList library compatible, as long as removes decision from DND side.

There is also another approach - replicate item.

  1. get ref to the item node
  2. create a clone of DOMTree by moved getComputedStyles' and getClientBoundaryRect+position:absolute`
  3. now you can move/display this clone in any place.

This is a relatively fast operation, but it would not so easy to mirror all possible changes in runtime to the clone, potentially "freezing" it. Which could be not a bad thing.

@alexreardon
Copy link
Collaborator Author

rbd could clone the item itself...
The main issue is letting the consumer control and use the snapshot.. πŸ€”

@alexreardon
Copy link
Collaborator Author

Right now a sensor is responsible for initiating a drag and movements during a drag. If we move to a clone strategy (which I think we will have to), then the sensor would only be responsible for starting a drag. Something else would need to manage the event listeners for a drag

@alexreardon alexreardon changed the title RFD: virtual list api Discussion: virtual list api Apr 11, 2019
@alexreardon alexreardon pinned this issue Apr 15, 2019
@DimitarNestorov
Copy link

I believe you should always render a clone in a virtual list, it would save you from a couple of headaches related to items disappearing. And the user should also be able to use the snapshot, to be able add a class or some other special thing to the item that is being dragged. What do you mean by control?

@alexreardon
Copy link
Collaborator Author

The lifecyle of the snapshot and other things gets a bit complicated when moving to a clone

@alexreardon
Copy link
Collaborator Author

I am playing with this. It looks like there will be a lot of heavy lifting

giphy-26

@alexreardon
Copy link
Collaborator Author

Whiteboarding the event listener problem with @kangweichan

2019-05-01 09 57 19

@alexreardon
Copy link
Collaborator Author

alexreardon commented May 1, 2019

// new export: VirtualDroppable
<VirtualDroppable
    droppableId="droppable"
    draggingItemClone={(provided: DraggableProvided, snapshot: DraggableStateSnapshot, source: DraggableLocation) => (
      <div {...provided.draggableProps} {...provided.dragHandleProps}>
        Dragging over {snapshot.draggingOver}
      </div>
    )}
  >
    {({provided: DroppableProvided} => (
      // 3rd party virtual list solution
      <VirtualList
        height={150}
        itemCount={1000}
        itemSize={35}
        width={300}
        innerRef={provided.innerRef}
      >
        {YourDraggableItem}
        {/* provided.placeholder is not required */}
      </List>
    )}
  </VirtualDroppable>
);

@tjramage
Copy link

tjramage commented May 4, 2019

@alexreardon β€” Wow. Myself and @jpsear are so excited to try this! Really awesome progress. Please let me know if / when you would like some testers, we'd be happy to help.

This was referenced May 6, 2019
@alexreardon
Copy link
Collaborator Author

I have created a Github project to track the details of this

https://github.com/atlassian/react-beautiful-dnd/projects/1

@alexreardon
Copy link
Collaborator Author

@tjramage i will let you know when I get closer. I hope to do a number of beta releases

@tjramage
Copy link

tjramage commented May 7, 2019

@alexreardon, awesome! Thanks for making your progress so transparent with the Github project, it's really great to see πŸ™

@euge
Copy link

euge commented May 8, 2019

@alexreardon Thank you thank you for making progress on this. The organization, discourse, and transparency of how this project is run...its inspiring. Also the use of emojis is πŸ”₯

@alexreardon
Copy link
Collaborator Author

alexreardon commented Jun 10, 2019

How to detect virtual lists?

I am deciding whether to try and auto-detect if a list is a virtual list or whether to use a mode="virtual" prop on Droppable.

react-window, react-virtualised and react-tiny-virtual-list all use the same technique:

  • position:absolute on each list item with a top/left property to control it's initial poisition
  • once a scroll location is reached on the list then remove / add item as needed with new left/top

the mode approach has setup footguns

  • if mode="virtual" then we will need a consumer to use the cloning api. We can add a warning or error for this
  • if mode = "virtual" and the list is not a virtual list - then things will break.

Autodetection could be brittle

  • from an item: check if it has position: absolute. But what if the position: absolute is applied to a parent? Do we either say it cannot be a parent - or walk up the DOM tree.
  • What if a library uses a different technique for positioning items?

More drastic 🀫

I am exploring an attempt to treat align the behaviour of virtual lists and standard lists so that no mode / auto detection is required. I am spiking this today

@DimitarNestorov
Copy link

DimitarNestorov commented Jun 11, 2019

What thing would break if the list is not virtual? Because that means that a list without many items (no need for scroll, or only just a bit of scroll but all items are rendered) is broken upon render.

I'm putting my bets on mode="virtual" with a big big big ⚠️⚠️⚠️WARNING⚠️⚠️⚠️ expressing any concerns in the docs. And a detector for dev mode if the list isn't virtual.

@tjramage
Copy link

Ahh that's good to know, thanks @alexreardon. It would definitely make the virtual list API a million times more useful for me, and a few others, by the sounds of it.

Wish I could buy you a beer!

@UmangThapliyal
Copy link

UmangThapliyal commented Jul 12, 2019

Hello, I am fairly new to this library but it is very useful and beautiful, I have a column which has more than 2k cards and I want to use this can you please point me the document where I can get some info about it. I was not able to find stories/virtual.
Also If I use without virtual list it takes some second to lift the card will it be resolved by using virtual-list?

@UmangThapliyal
Copy link

Hello I tried the above provided code snippet and got the below error
image

@kangweichan
Copy link

@UmangThapliyal are you using version 12.0.0-alpha.2? The virtual list support API hasn't been officially released yet, but is currently in alpha version at the version string I mentioned above.

See more details here: #1317

@UmangThapliyal
Copy link

@UmangThapliyal are you using version 12.0.0-alpha.2? The virtual list support API hasn't been officially released yet, but is currently in alpha version at the version string I mentioned above.

See more details here: #1317

Yes I was trying out 12.0.0-alpha.2 and faced the issue

@saaage
Copy link

saaage commented Jul 16, 2019

Will this increase performance for @atlaskit/tree?

@alexreardon
Copy link
Collaborator Author

Latest alpha: 12.0.0-alpha.7

@UmangThapliyal
Copy link

Latest alpha: 12.0.0-alpha.7

Does it support autosizer and items having different sizes

@alexreardon
Copy link
Collaborator Author

Untested, but should be fine 🀞

@UmangThapliyal
Copy link

Untested, but should be fine 🀞

Thanks will check it out
VirtualDroppable will work in 12.0.0-alpha.7?

@alexreardon
Copy link
Collaborator Author

VirtualDroppable is not a thing. Use this:

<Droppable 
  droppableId="droppable"
  mode="VIRTUAL"
  whenDraggingClone={(
  provided: DraggableProvided,
  snapshot: DraggableStateSnapshot,
  source: DraggableLocation,
 ) => React.Node}
>

</Droppable

@UmangThapliyal
Copy link

UmangThapliyal commented Jul 22, 2019

VirtualDroppable is not a thing. Use this:

<Droppable 
  droppableId="droppable"
  mode="VIRTUAL"
  whenDraggingClone={(
  provided: DraggableProvided,
  snapshot: DraggableStateSnapshot,
  source: DraggableLocation,
 ) => React.Node}
>

</Droppable

Oh thanks I was trying with this only but reading the above comment I got the doubt thanks will try it.

@alexreardon
Copy link
Collaborator Author

Docs have not been written for this part yet

@UmangThapliyal
Copy link

UmangThapliyal commented Jul 22, 2019

Docs have not been written for this part yet

Yeah, I was trying the example from stories/virtual, will try it out more and will post back if have any doubts.
BTW kudos for such a great library it is really helpful

@suanmei
Copy link

suanmei commented Jul 23, 2019

@alexreardon How long will the official version be released? I can't wait to use this great feature

@suanmei
Copy link

suanmei commented Aug 26, 2019

@alexreardon look forward to your reply

@alexreardon
Copy link
Collaborator Author

I am trudging through some issues that have been raised in testing. No firm eta at this point. I hope to have a new alpha out soon

@pathikdevani
Copy link

@alexreardon can you help to understand how exactly dnd is working with virtualization? I am using react-dnd & react-window in my project and trying to simulate the same.

I wanna know what is the thought process to make support dnd with virtualization?

Thanks in advance.

@alexreardon
Copy link
Collaborator Author

https://github.com/atlassian/react-beautiful-dnd/blob/dev/docs/patterns/virtual-lists.md

@alexreardon
Copy link
Collaborator Author

I hope to do a talk about the how later. It has been a large journey

@pathikdevani
Copy link

@alexreardon I have already gone through virtual-lists patterns doc. It's very useful as a user of rbd. I know how is a big ask. But if you write some doc on how , it will be very helpful and it will make independent logic, that will be beyod rbd.

Thanks, I will wait for how doc.

@suanmei
Copy link

suanmei commented Oct 8, 2019

Hi, is there any API about the Persistent

@alexreardon
Copy link
Collaborator Author

alexreardon commented Oct 8, 2019

Hi, is there any API about the Persistent

I'm not sure what this means

@suanmei
Copy link

suanmei commented Oct 9, 2019

Persistent?
We could get away from needing a clone if we could mark an item in a virtual list as persistent after a drag starts so the react component is never unmounted.

Just this

@alexreardon alexreardon unpinned this issue Oct 10, 2019
@alexreardon
Copy link
Collaborator Author

Using Persistent is fine. Not every major virtual list library out there supports that feature so I didn't lean on it

@suanmei
Copy link

suanmei commented Nov 7, 2019

@alexreardon You mentioned in this issue #216 that you will support clone mode. And I think RBDβ€˜s core is drag and drop🀣, most dnd libraries support this ability

@alexreardon
Copy link
Collaborator Author

We'll track dragging a copy in #216

@alexreardon
Copy link
Collaborator Author

This shipped in #1487 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests