Skip to content

Commit 0ee1593

Browse files
committed
perf(cdk/drag-drop): fix performance regression when destroying items (#30751)
#30514 changed the logic that syncs destroyed items to apply to non-dragged items as well. This led to a performance regression where swapping out a large list of items can lock up the entire browser. The problem is that the items need to be re-sorted each time an item is destroyed which is expensive. These changes resolve the issue by keeping track of the last set of items and dropping the item from it without re-sorting. Fixes #30737. (cherry picked from commit 1372f52)
1 parent c14ff14 commit 0ee1593

File tree

1 file changed

+19
-5
lines changed

1 file changed

+19
-5
lines changed

src/cdk/drag-drop/directives/drop-list.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ export class CdkDropList<T = any> implements OnDestroy {
5959
skipSelf: true,
6060
});
6161

62+
/** Refs that have been synced with the drop ref most recently. */
63+
private _latestSortedRefs: DragRef[] | undefined;
64+
6265
/** Emits when the list has been destroyed. */
6366
private readonly _destroyed = new Subject<void>();
6467

@@ -222,7 +225,7 @@ export class CdkDropList<T = any> implements OnDestroy {
222225
// Only sync the items while dragging since this method is
223226
// called when items are being initialized one-by-one.
224227
if (this._dropListRef.isDragging()) {
225-
this._syncItemsWithRef();
228+
this._syncItemsWithRef(this.getSortedItems().map(item => item._dragRef));
226229
}
227230
}
228231

@@ -231,7 +234,16 @@ export class CdkDropList<T = any> implements OnDestroy {
231234
this._unsortedItems.delete(item);
232235

233236
// This method might be called on destroy so we always want to sync with the ref.
234-
this._syncItemsWithRef();
237+
// Note that we reuse the last set of synced items, rather than re-sorting the whole
238+
// list, because it can slow down re-renders of large lists (see #30737).
239+
if (this._latestSortedRefs) {
240+
const index = this._latestSortedRefs.indexOf(item._dragRef);
241+
242+
if (index > -1) {
243+
this._latestSortedRefs.splice(index, 1);
244+
this._syncItemsWithRef(this._latestSortedRefs);
245+
}
246+
}
235247
}
236248

237249
/** Gets the registered items in the list, sorted by their position in the DOM. */
@@ -259,6 +271,7 @@ export class CdkDropList<T = any> implements OnDestroy {
259271
this._group._items.delete(this);
260272
}
261273

274+
this._latestSortedRefs = undefined;
262275
this._unsortedItems.clear();
263276
this._dropListRef.dispose();
264277
this._destroyed.next();
@@ -335,7 +348,7 @@ export class CdkDropList<T = any> implements OnDestroy {
335348
/** Handles events from the underlying DropListRef. */
336349
private _handleEvents(ref: DropListRef<CdkDropList>) {
337350
ref.beforeStarted.subscribe(() => {
338-
this._syncItemsWithRef();
351+
this._syncItemsWithRef(this.getSortedItems().map(item => item._dragRef));
339352
this._changeDetectorRef.markForCheck();
340353
});
341354

@@ -403,7 +416,8 @@ export class CdkDropList<T = any> implements OnDestroy {
403416
}
404417

405418
/** Syncs up the registered drag items with underlying drop list ref. */
406-
private _syncItemsWithRef() {
407-
this._dropListRef.withItems(this.getSortedItems().map(item => item._dragRef));
419+
private _syncItemsWithRef(items: DragRef[]) {
420+
this._latestSortedRefs = items;
421+
this._dropListRef.withItems(items);
408422
}
409423
}

0 commit comments

Comments
 (0)