From 99db7b879aa334a53f500598f89054508468151b Mon Sep 17 00:00:00 2001 From: Josh Kelley Date: Tue, 22 Mar 2022 17:33:36 -0400 Subject: [PATCH 1/3] fix: "Invariant Violation: Expected targetIds to be registered." We've sporadically seen this error from customers. The call stack indicates that it's originating from HTML5BackendImpl's handleTopDragOver's requestAnimationFrame callback. I've been unable to reproduce it locally; however, if I simulate a slowdown by replacing `requestAnimationFrame(callback)` with `setTimeout(callback, 10000)`, I can fairly reliably reproduce this error. To fix it, I believe HTML5BackendImpl should consistently clear the hover animation whenever the drag operation is ended for any reason. I locally tested this fix in the v15.1.2 tag, and it appeared to work. Fixes #763, #3403 --- CONTRIBUTING.md | 2 +- .../backend-html5/src/HTML5BackendImpl.ts | 47 +++++++++++++------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bbc62f4abe..c4319f133e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to React-DnD -So you want to contibute to React-DnD? Thank you! This library is a community effort, and your contributions are greatly appreciated. +So you want to contribute to React-DnD? Thank you! This library is a community effort, and your contributions are greatly appreciated. ## FAQ diff --git a/packages/backend-html5/src/HTML5BackendImpl.ts b/packages/backend-html5/src/HTML5BackendImpl.ts index 9cd8519edc..e38e0c528c 100644 --- a/packages/backend-html5/src/HTML5BackendImpl.ts +++ b/packages/backend-html5/src/HTML5BackendImpl.ts @@ -347,6 +347,7 @@ export class HTML5BackendImpl implements Backend { if (this.clearCurrentDragSourceNode() && this.monitor.isDragging()) { this.actions.endDrag() } + this.cancelHover() } private setCurrentDragSourceNode(node: Element | null) { @@ -400,6 +401,33 @@ export class HTML5BackendImpl implements Backend { return false } + private scheduleHover = (dragOverTargetIds: string[] | null) => { + if ( + this.hoverRafId === null && + typeof requestAnimationFrame !== 'undefined' + ) { + this.hoverRafId = requestAnimationFrame(() => { + if (this.monitor.isDragging()) { + this.actions.hover(dragOverTargetIds || [], { + clientOffset: this.lastClientOffset, + }) + } + + this.hoverRafId = null + }) + } + } + + private cancelHover = () => { + if ( + this.hoverRafId !== null && + typeof cancelAnimationFrame !== 'undefined' + ) { + cancelAnimationFrame(this.hoverRafId) + this.hoverRafId = null + } + } + public handleTopDragStartCapture = (): void => { this.clearCurrentDragSourceNode() this.dragStartSourceIds = [] @@ -429,6 +457,7 @@ export class HTML5BackendImpl implements Backend { // Avoid crashing if we missed a drop event or our previous drag died if (this.monitor.isDragging()) { this.actions.endDrag() + this.cancelHover() } // Don't publish the source just yet (see why below) @@ -530,6 +559,7 @@ export class HTML5BackendImpl implements Backend { // Only proceed if we have not handled it already. this.actions.endDrag() } + this.cancelHover() } public handleTopDragEnterCapture = (e: DragEvent): void => { @@ -622,20 +652,7 @@ export class HTML5BackendImpl implements Backend { this.altKeyPressed = e.altKey this.lastClientOffset = getEventClientOffset(e) - if ( - this.hoverRafId === null && - typeof requestAnimationFrame !== 'undefined' - ) { - this.hoverRafId = requestAnimationFrame(() => { - if (this.monitor.isDragging()) { - this.actions.hover(dragOverTargetIds || [], { - clientOffset: this.lastClientOffset, - }) - } - - this.hoverRafId = null - }) - } + this.scheduleHover(dragOverTargetIds) const canDrop = (dragOverTargetIds || []).some((targetId) => this.monitor.canDropOnTarget(targetId), @@ -672,6 +689,7 @@ export class HTML5BackendImpl implements Backend { if (this.isDraggingNativeItem()) { setTimeout(() => this.endDragNativeItem(), 0) } + this.cancelHover() } public handleTopDropCapture = (e: DragEvent): void => { @@ -709,6 +727,7 @@ export class HTML5BackendImpl implements Backend { } else if (this.monitor.isDragging()) { this.actions.endDrag() } + this.cancelHover() } public handleSelectStart = (e: DragEvent): void => { From f8ba3ee050c123aeb8b814bb07c564e99e89e42f Mon Sep 17 00:00:00 2001 From: Chris Trevino Date: Mon, 28 Mar 2022 16:35:05 -0700 Subject: [PATCH 2/3] chore: semver --- .github/workflows/ci.yml | 6 +----- .github/workflows/version-check.yml | 16 ++++++++++++++++ .yarn/versions/e42d7e63.yml | 9 +++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/version-check.yml create mode 100644 .yarn/versions/e42d7e63.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb71b2d159..72a71f8359 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,11 +28,7 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - - - run: yarn version check - if: "github.actor != 'dependabot[bot]' && !contains(github.ref , 'release/') && !contains(github.head_ref , 'release/')" - name: Version Check - + - run: yarn install name: Install Dependencies diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml new file mode 100644 index 0000000000..46d146966c --- /dev/null +++ b/.github/workflows/version-check.yml @@ -0,0 +1,16 @@ +name: Version Check +on: [push, pull_request] +env: + DEFAULT_NODE_VERSION: 16 +jobs: + version-check: + runs-on: ubuntu-latest + if: "github.ref != 'main' && github.head_ref != 'main' && !contains(github.ref , 'release/') && !contains(github.head_ref , 'release/') && !contains(github.ref , 'dependabot/') && !contains(github.head_ref , 'dependabot/')" + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: actions/setup-node@v1 + with: + node-version: ${{env.DEFAULT_NODE_VERSION}} + - run: yarn version check diff --git a/.yarn/versions/e42d7e63.yml b/.yarn/versions/e42d7e63.yml new file mode 100644 index 0000000000..4ea38cde91 --- /dev/null +++ b/.yarn/versions/e42d7e63.yml @@ -0,0 +1,9 @@ +releases: + react-dnd-html5-backend: patch + +declined: + - react-dnd-documentation + - react-dnd-examples + - test-suite-cra + - test-suite-vite + - react-dnd-test-utils From c901c10bfc00a169536e4e554c2ae850f993eee2 Mon Sep 17 00:00:00 2001 From: Chris Trevino Date: Mon, 28 Mar 2022 17:18:54 -0700 Subject: [PATCH 3/3] fix: cancel any raf before creating a new one --- packages/backend-html5/src/HTML5BackendImpl.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/backend-html5/src/HTML5BackendImpl.ts b/packages/backend-html5/src/HTML5BackendImpl.ts index e38e0c528c..f509841aba 100644 --- a/packages/backend-html5/src/HTML5BackendImpl.ts +++ b/packages/backend-html5/src/HTML5BackendImpl.ts @@ -406,6 +406,9 @@ export class HTML5BackendImpl implements Backend { this.hoverRafId === null && typeof requestAnimationFrame !== 'undefined' ) { + // cancel any existing hover if present + this.cancelHover() + this.hoverRafId = requestAnimationFrame(() => { if (this.monitor.isDragging()) { this.actions.hover(dragOverTargetIds || [], {