fix(kanban): infinite ref-merge loop + drag lag
Two issues: 1. "Maximum update depth exceeded" inside Mantine's useMergedRef during drag. Root cause: the `data` object passed to dnd-kit's `useSortable` in KanbanCard and KanbanColumn was re-created on every render. Mantine Paper composes its internal ref with the consumer ref via useMergedRef, which uses a useCallback whose deps array contains the refs themselves. Whenever the underlying setNodeRef from useSortable became unstable (because dnd-kit's internal state churned on data identity change), the merged ref was reassigned each commit -> setState inside the ref callback -> next render -> new data identity -> loop. Wrap the sortable data in useMemo keyed on its real inputs. 2. Drag feels laggy. Each card listens to a 1-second `now` clock that re-renders the entire board to refresh the "time in column" label. Pause that interval while a drag is active so dnd-kit's per-pixel reconciliation does not also re-mount/re-layout every card every second. Tick resumes the moment the drag ends. Also move the Select `data` array for the column max-time popover from an inline expression to a module-level constant; same array identity across all column instances and renders -> Mantine Combobox stops re-running its diffing effect for free. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ import {
|
||||
IconUserCircle,
|
||||
} from "@tabler/icons-react";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { memo, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { Card, CardColor, User } from "../types";
|
||||
import { colorBg, colorBorder, tagColor } from "./colors";
|
||||
import { ColorPickerGrid } from "./ColorPickerGrid";
|
||||
@@ -101,9 +101,17 @@ function KanbanCardImpl({
|
||||
const cardElRef = useRef<HTMLElement | null>(null);
|
||||
const draggingStickerRef = useRef<number | null>(null);
|
||||
const stickerMode = !!activeSticker;
|
||||
// Memo: useSortable es sensible a la identidad del objeto `data`. Si lo
|
||||
// re-creamos cada render, el setNodeRef interno se vuelve inestable y
|
||||
// dispara loops por useMergedRef de Mantine (Paper). Issue: maximum
|
||||
// update depth visto durante drag.
|
||||
const sortableData = useMemo(
|
||||
() => ({ type: "card" as const, columnId: card.column_id, locked: card.locked }),
|
||||
[card.column_id, card.locked]
|
||||
);
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: card.id,
|
||||
data: { type: "card", columnId: card.column_id, locked: card.locked },
|
||||
data: sortableData,
|
||||
disabled: stickerMode,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user