1212
1313import { AriaButtonProps } from '@react-types/button' ;
1414import { 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' ;
1616import * as DragManager from './DragManager' ;
1717import { DROP_EFFECT_TO_DROP_OPERATION , DROP_OPERATION , EFFECT_ALLOWED } from './constants' ;
1818import { globalDropEffect , setGlobalAllowedDropOperations , setGlobalDropEffect , useDragModality , writeToDataTransfer } from './utils' ;
1919// @ts -ignore
2020import 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' ;
2222import { useLocalizedStringFormatter } from '@react-aria/i18n' ;
2323
2424export interface DragOptions {
@@ -82,11 +82,11 @@ export function useDrag(options: DragOptions): DragResult {
8282 y : 0
8383 } ) . current ;
8484 state . options = options ;
85- let isDraggingRef = useRef ( false ) ;
85+ let isDraggingRef = useRef < Element | null > ( null ) ;
8686 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 ) ;
9090 } ;
9191 let { addGlobalListener, removeAllGlobalListeners} = useGlobalListeners ( ) ;
9292 let modalityOnPointerDown = useRef < string > ( null ) ;
@@ -186,8 +186,9 @@ export function useDrag(options: DragOptions): DragResult {
186186
187187 // Wait a frame before we set dragging to true so that the browser has time to
188188 // render the preview image before we update the element that has been dragged.
189+ let target = e . target ;
189190 requestAnimationFrame ( ( ) => {
190- setDragging ( true ) ;
191+ setDragging ( target as Element ) ;
191192 } ) ;
192193 } ;
193194
@@ -231,7 +232,7 @@ export function useDrag(options: DragOptions): DragResult {
231232 options . onDragEnd ( event ) ;
232233 }
233234
234- setDragging ( false ) ;
235+ setDragging ( null ) ;
235236 removeAllGlobalListeners ( ) ;
236237 setGlobalAllowedDropOperations ( DROP_OPERATION . none ) ;
237238 setGlobalDropEffect ( undefined ) ;
@@ -240,9 +241,12 @@ export function useDrag(options: DragOptions): DragResult {
240241 // If the dragged element is removed from the DOM via onDrop, onDragEnd won't fire: https://bugzilla.mozilla.org/show_bug.cgi?id=460801
241242 // In this case, we need to manually call onDragEnd on cleanup
242243
243- useLayoutEffect ( ( ) => {
244+ useEffect ( ( ) => {
244245 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 ) ) {
246250 if ( typeof state . options . onDragEnd === 'function' ) {
247251 let event : DragEndEvent = {
248252 type : 'dragend' ,
@@ -253,7 +257,7 @@ export function useDrag(options: DragOptions): DragResult {
253257 state . options . onDragEnd ( event ) ;
254258 }
255259
256- setDragging ( false ) ;
260+ setDragging ( null ) ;
257261 setGlobalAllowedDropOperations ( DROP_OPERATION . none ) ;
258262 setGlobalDropEffect ( undefined ) ;
259263 }
@@ -285,14 +289,14 @@ export function useDrag(options: DragOptions): DragResult {
285289 ? state . options . getAllowedDropOperations ( )
286290 : [ 'move' , 'copy' , 'link' ] ,
287291 onDragEnd ( e ) {
288- setDragging ( false ) ;
292+ setDragging ( null ) ;
289293 if ( typeof state . options . onDragEnd === 'function' ) {
290294 state . options . onDragEnd ( e ) ;
291295 }
292296 }
293297 } , stringFormatter ) ;
294298
295- setDragging ( true ) ;
299+ setDragging ( target ) ;
296300 } ;
297301
298302 let modality = useDragModality ( ) ;
0 commit comments