dnd-kit icon indicating copy to clipboard operation
dnd-kit copied to clipboard

re-rendering ALL draggable items on drag start and such

Open alissaVrk opened this issue 1 year ago • 19 comments

it seems that all draggable components will re-render when one of them is dragged, it happens because of the use of context in useDraggable and the way that the context exposes the values, for example, active (which definitely changes when you start dragging).

also need to change from exposing the active element on the context to isDragging or myActiveInfo so that components that aren't active won't get a new value that they don't need (with my solution it will be useIsDraggable or useMyActiveInfo)

 const {
    activators,
    activatorEvent,
    active,
    activeNodeRect,
    ariaDescribedById,
    draggableNodes,
    over,
  } = useContext(InternalContext);

there is a way around it. the idea is to expose functions instead of values on the context value so it won't re-render all components that use it when something that is irrelevant to them changes.

I wrote a blog post on the subject.

it probably happens in many other cases.. like a change in over

If you like the idea I can try and prepare a PR

the problem is also that you expose active to the user of useDraggable and if we do the change I propose non-active components won't have that information - it will change the API a bit

alissaVrk avatar Mar 18 '23 07:03 alissaVrk

Same issue here. Anytime I drag start the entire page rerenders. This is a problem on pages with 100+ items, the page freezes for 1s+

jonahallibone avatar Mar 21 '23 02:03 jonahallibone

@jonahallibone a workaround for this issue could be creating a Draggable component that will receive the content (the actual component) as children. but then you cannot pass isDraggable and such to it - sandbox All the Draggable components will still re-render on drag start and such, but the Item won't

Or you can separate the draggable item into 2 components: DraggableItem and Item + memo the Item. This way you can pass whichever props you like, but it has the disadvantages of memoization - sandbox All the DraggableItem components will still re-render on drag start and such, but the Item won't

don't get me wrong, I still think that the right thing to do is not to re-render all of them

alissaVrk avatar Mar 21 '23 05:03 alissaVrk

@alissaVrk thanks! that helped a bit!

jonahallibone avatar Mar 21 '23 17:03 jonahallibone

@alissaVrk a significant amount of performance-based open issues are both directly and indirectly related to this (e.g., #994, #943, #898, ...). A PR would be highly appreciated!

tomasmenezes avatar Mar 28 '23 15:03 tomasmenezes

@tomasmenezes I will try to create a first draft for next week

alissaVrk avatar Mar 28 '23 17:03 alissaVrk

@tomasmenezes PR ready, only for active. it won't solve most of the issues you mentioned, to solve them we need to change useSortable as well.

useSortable exposes activeIndex and overIndex which will cause a re-render for all items even with my fix. it is possible to solve but it will require a change in the API. for example, we can expose over: 'before-me' | 'me' | 'after-me' | 'other' on useSortable and add another hook for a more generic use - useSortableContext which will expose overIndex and such. There might be more issues there, I didn't dig deep into the sortable yet

alissaVrk avatar Mar 30 '23 06:03 alissaVrk

@tomasmenezes PR for drappable and sortable is ready there are quite a few changes in the API, but I couldn't avoid it..

All items will still re-render if the passed items to the sortable context change, happens mostly on drop, probably fixable as well, but I had to stop at some point :)

tested with the example in #994, seems to solve the issue. if you know of more tickets with reproductions, let me know, will check the impact

alissaVrk avatar Apr 05 '23 06:04 alissaVrk

@alissaVrk Looks awesome! Will also test it out later this week. I think API changes are welcome to deal with issues as big as this one. I think most people here would encourage you to keep on going 😄

tomasmenezes avatar Apr 06 '23 10:04 tomasmenezes

@tomasmenezes, If this PR will get enough attention to actually get merged, maybe I will :)

alissaVrk avatar Apr 06 '23 12:04 alissaVrk

@alissaVrk great work! i hope @clauderic will eventually continue to work on this lib. unfortunately there has been no activity for several months

kyeo76 avatar Apr 06 '23 16:04 kyeo76

@tomasmenezes @kyeo76 Thank you for the feedback :)

alissaVrk avatar Apr 07 '23 07:04 alissaVrk

@alissaVrk Thanks for working on this! I'm brand new to dnd-kit, but this is the first thing I noticed - that all draggable and droppable components are re-rendering on start, over and end.

stevenkkim avatar Apr 08 '23 04:04 stevenkkim

any updates on this?

sophia-kravchenko avatar Jun 05 '23 15:06 sophia-kravchenko

Any updates on this? I am facing performance issues with sortableContext when there are more than 500 components.

sheminanto avatar Jun 08 '23 09:06 sheminanto

@sophia-kravchenko @sheminanto doesn't seem like it :( but you can try the workaround I suggested in the 3rd comment to this issue, it might help a bit

alissaVrk avatar Jun 11 '23 03:06 alissaVrk

I am facing this same issue, thanks @alissaVrk for the workaround and @clauderic for this amazing library! It would be awesome if the PR finally gets merged in new versions

Julibnk avatar Jul 11 '23 13:07 Julibnk

I've used react-tracked (https://github.com/dai-shi/react-tracked) on projects that use context with great results. It acts as a proxy between the context and components consuming it - only triggering re-renders to components consuming the parts of the context that have changed.

I was curious if anyone has tried it (also, there's a few other libraries that do something similar)?

Jordan-Eckowitz avatar Jan 26 '24 18:01 Jordan-Eckowitz

I've used react-tracked (https://github.com/dai-shi/react-tracked) on projects that use context with great results. It acts as a proxy between the context and components consuming it - only triggering re-renders to components consuming the parts of the context that have changed.

I was curious if anyone has tried it (also, there's a few other libraries that do something similar)?

Great inspiration, I haven’t tried, but I also know that https://github.com/dai-shi/use-context-selector can achieve too. I will try it later today, thanks for the solution. And I’m also curious about the status of the PR above, since it hasn’t updated for a long time ; (

MaxtuneLee avatar Jan 27 '24 05:01 MaxtuneLee

@jonahallibone a workaround for this issue could be creating a Draggable component that will receive the content (the actual component) as children. but then you cannot pass isDraggable and such to it

You can if you use cloneElement. I've memoized it to make sure it doesn't constantly clone 100s of elements.

const clonedChild = useMemo(
    () =>
      cloneElement(children, {
        isDragging,
      }),
    [children, id, isDragging]
  )

  return (
    <div
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
    >
      {clonedChild}
    </div>
  )

ZachMayry avatar May 31 '24 21:05 ZachMayry