12
12
13
13
import { AriaButtonProps } from '@react-types/button' ;
14
14
import { DragEndEvent , DragItem , DragMoveEvent , DragPreviewRenderer , DragStartEvent , DropOperation , PressEvent , RefObject } from '@react-types/shared' ;
15
- import { DragEvent , HTMLAttributes , useRef , useState } from 'react' ;
15
+ import { DragEvent , HTMLAttributes , version as ReactVersion , useEffect , useRef , useState } from 'react' ;
16
16
import * as DragManager from './DragManager' ;
17
17
import { DROP_EFFECT_TO_DROP_OPERATION , DROP_OPERATION , EFFECT_ALLOWED } from './constants' ;
18
18
import { globalDropEffect , setGlobalAllowedDropOperations , setGlobalDropEffect , useDragModality , writeToDataTransfer } from './utils' ;
19
19
// @ts -ignore
20
20
import intlMessages from '../intl/*.json' ;
21
- import { isVirtualClick , isVirtualPointerEvent , useDescription , useGlobalListeners , useLayoutEffect } from '@react-aria/utils' ;
21
+ import { isVirtualClick , isVirtualPointerEvent , useDescription , useGlobalListeners } from '@react-aria/utils' ;
22
22
import { useLocalizedStringFormatter } from '@react-aria/i18n' ;
23
23
24
24
export interface DragOptions {
@@ -82,11 +82,11 @@ export function useDrag(options: DragOptions): DragResult {
82
82
y : 0
83
83
} ) . current ;
84
84
state . options = options ;
85
- let isDraggingRef = useRef ( false ) ;
85
+ let isDraggingRef = useRef < Element | null > ( null ) ;
86
86
let [ isDragging , setDraggingState ] = useState ( false ) ;
87
- let setDragging = ( isDragging ) => {
88
- isDraggingRef . current = isDragging ;
89
- setDraggingState ( isDragging ) ;
87
+ let setDragging = ( element : Element | null ) => {
88
+ isDraggingRef . current = element ;
89
+ setDraggingState ( ! ! element ) ;
90
90
} ;
91
91
let { addGlobalListener, removeAllGlobalListeners} = useGlobalListeners ( ) ;
92
92
let modalityOnPointerDown = useRef < string > ( null ) ;
@@ -186,8 +186,9 @@ export function useDrag(options: DragOptions): DragResult {
186
186
187
187
// Wait a frame before we set dragging to true so that the browser has time to
188
188
// render the preview image before we update the element that has been dragged.
189
+ let target = e . target ;
189
190
requestAnimationFrame ( ( ) => {
190
- setDragging ( true ) ;
191
+ setDragging ( target as Element ) ;
191
192
} ) ;
192
193
} ;
193
194
@@ -231,7 +232,7 @@ export function useDrag(options: DragOptions): DragResult {
231
232
options . onDragEnd ( event ) ;
232
233
}
233
234
234
- setDragging ( false ) ;
235
+ setDragging ( null ) ;
235
236
removeAllGlobalListeners ( ) ;
236
237
setGlobalAllowedDropOperations ( DROP_OPERATION . none ) ;
237
238
setGlobalDropEffect ( undefined ) ;
@@ -240,9 +241,12 @@ export function useDrag(options: DragOptions): DragResult {
240
241
// If the dragged element is removed from the DOM via onDrop, onDragEnd won't fire: https://bugzilla.mozilla.org/show_bug.cgi?id=460801
241
242
// In this case, we need to manually call onDragEnd on cleanup
242
243
243
- useLayoutEffect ( ( ) => {
244
+ useEffect ( ( ) => {
244
245
return ( ) => {
245
- if ( isDraggingRef . current ) {
246
+ // Check that the dragged element has actually unmounted from the DOM and not a React Strict Mode false positive.
247
+ // https://github.com/facebook/react/issues/29585
248
+ // React 16 ran effect cleanups before removing elements from the DOM but did not have this issue.
249
+ if ( isDraggingRef . current && ( ! isDraggingRef . current . isConnected || parseInt ( ReactVersion , 10 ) < 17 ) ) {
246
250
if ( typeof state . options . onDragEnd === 'function' ) {
247
251
let event : DragEndEvent = {
248
252
type : 'dragend' ,
@@ -253,7 +257,7 @@ export function useDrag(options: DragOptions): DragResult {
253
257
state . options . onDragEnd ( event ) ;
254
258
}
255
259
256
- setDragging ( false ) ;
260
+ setDragging ( null ) ;
257
261
setGlobalAllowedDropOperations ( DROP_OPERATION . none ) ;
258
262
setGlobalDropEffect ( undefined ) ;
259
263
}
@@ -285,14 +289,14 @@ export function useDrag(options: DragOptions): DragResult {
285
289
? state . options . getAllowedDropOperations ( )
286
290
: [ 'move' , 'copy' , 'link' ] ,
287
291
onDragEnd ( e ) {
288
- setDragging ( false ) ;
292
+ setDragging ( null ) ;
289
293
if ( typeof state . options . onDragEnd === 'function' ) {
290
294
state . options . onDragEnd ( e ) ;
291
295
}
292
296
}
293
297
} , stringFormatter ) ;
294
298
295
- setDragging ( true ) ;
299
+ setDragging ( target ) ;
296
300
} ;
297
301
298
302
let modality = useDragModality ( ) ;
0 commit comments