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

Profiler Highlights All Items When Interacting with a Single Draggable Item #1563

Open
NavTheRaj opened this issue Dec 11, 2024 · 2 comments

Comments

@NavTheRaj
Copy link

NavTheRaj commented Dec 11, 2024

Title

Profiler Highlights All Items When Interacting with a Single Draggable Item


Description

Using the React DevTools Profiler, I noticed that when I interact with the drag button of a single item in a sortable list, all items in the list get highlighted in the Profiler, indicating that they are re-rendering. However, when adding a console log in the draggable button (DraggableIconButton), only the interacted button logs a message, suggesting that only one component is truly being re-rendered.

This discrepancy between the Profiler's highlight and the actual logs is confusing. I want to confirm whether this is expected behavior due to React's reconciliation process or a bug in @dnd-kit.


Steps to Reproduce

  1. Create a sortable list using @dnd-kit/core and @dnd-kit/sortable.
  2. Wrap all draggable components with React.memo to prevent unnecessary re-renders.
  3. Use the React DevTools Profiler to observe updates when interacting with a single drag button.
  4. Click or drag one item's button.

Expected Behavior:
The Profiler highlights only the interacted item if it re-renders.

Actual Behavior:
The Profiler highlights all sibling items, even though only one of the list item is touched/clicked. I am worried about when the list grows up and these re renders become very expensive.

Already tried

  • Use Context Selector to handle the context
  • Memoizing the listeners and attributes
  • Memoizing the internal sortable list item
  • Using useSortable outside the component
  • Callbacks to wrap the remove functionality

Reproduction Code

Here’s a simplified version of the setup:

App.tsx

import React, { useState } from "react";
import { DndContext, SortableContext } from "@dnd-kit/core";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

const DraggableIconButton = React.memo(({ listeners, attributes, id }: any) => {
  console.log(`DraggableIconButton for id=${id} rendered.`);
  return (
    <button {...(listeners ?? {})} {...(attributes ?? {})} style={{ cursor: "grab" }}>
      Drag
    </button>
  );
});

const SortableItem = React.memo(({ id }: { id: string }) => {
  const { setNodeRef, transform, transition, listeners, attributes } = useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    padding: "10px",
    marginBottom: "5px",
    backgroundColor: "white",
    border: "1px solid lightgray",
    display: "flex",
    alignItems: "center",
  };

  return (
    <div ref={setNodeRef} style={style}>
      {id}
      <DraggableIconButton listeners={listeners} attributes={attributes} id={id} />
    </div>
  );
});

export default function App() {
  const [items, setItems] = useState(["Item 1", "Item 2", "Item 3"]);

  return (
    <DndContext>
      <SortableContext items={items}>
        <div>
          {items.map((item) => (
            <SortableItem key={item} id={item} />
          ))}
        </div>
      </SortableContext>
    </DndContext>
  );
}

P.S Great Library and really easy to use. I would really appreciate if someone could guide me on this one.

Profiler showing all three item list being re rendered

image

Rendering Highlights on click to one of the item list

image

List of Hooks for an idea of whats being changed

image

Sandbox Link

React TypeScript Sandbox

@NavTheRaj
Copy link
Author

NavTheRaj commented Dec 11, 2024

@clauderic Could you please give some light on this issue ?

Thank You !

@mikister
Copy link

Hi @NavTheRaj.
What's happening here is that when you drag one of the items it changes the state in causing it's children to re-render, hence the highlighting in profiler.
But since you wrapped the items in React.memo and their props haven't actually changed they don't get re-rendered. The only exception is the item being currently dragged which has the output of one of it's hooks changed causing a re-render.

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

No branches or pull requests

2 participants