11"use client" ;
22
3- import { useEffect , useRef } from "react" ;
3+ import { useEffect } from "react" ;
44import { monitorForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter" ;
55import { extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge" ;
66import { DashboardGroup } from "../../types" ;
@@ -10,16 +10,8 @@ import { DashboardGroup } from "../../types";
1010 * Handles reordering items within and between groups.
1111 */
1212export default function useDashboardDragDrop (
13- userDashboard : DashboardGroup [ ] ,
1413 setUserDashboard : React . Dispatch < React . SetStateAction < DashboardGroup [ ] > > ,
1514) {
16- // Keep a ref to the latest dashboard so the monitor effect does not need
17- // to re-subscribe every time the dashboard changes.
18- const userDashboardRef = useRef ( userDashboard ) ;
19- useEffect ( ( ) => {
20- userDashboardRef . current = userDashboard ;
21- } , [ userDashboard ] ) ;
22-
2315 useEffect ( ( ) => {
2416 return monitorForElements ( {
2517 onDrop ( { source, location } ) {
@@ -29,100 +21,85 @@ export default function useDashboardDragDrop(
2921 }
3022 const sourceData = source . data ;
3123 const targetData = target . data ;
32- const currentDashboard = userDashboardRef . current ;
24+ const sourceTitle = sourceData . title as string ;
25+ const destinationTitle = targetData . title as string ;
26+ const sourceIndex = sourceData . index as number ;
3327
3428 if ( location . current . dropTargets . length === 2 ) {
35- const groupTitle = targetData . title ;
3629 const closestEdgeOfTarget = extractClosestEdge ( targetData ) ;
3730 const targetIndex = targetData . index as number ;
38- const sourceGroup = currentDashboard . find (
39- ( group ) => group . title === sourceData . title ,
40- ) ;
41- const targetGroup = currentDashboard . find (
42- ( group ) => group . title === groupTitle ,
43- ) ;
44- const sourceIndex = sourceData . index as number ;
4531 const destinationIndex = (
4632 closestEdgeOfTarget === "top" ? targetIndex : targetIndex + 1
4733 ) as number ;
4834
4935 reorderSections (
50- sourceGroup ,
51- targetGroup ,
36+ sourceTitle ,
37+ destinationTitle ,
5238 sourceIndex ,
5339 destinationIndex ,
5440 ) ;
5541 } else {
56- const groupTitle = targetData . title ;
57- const sourceGroup = currentDashboard . find (
58- ( group ) => group . title === sourceData . title ,
59- ) ;
60- const targetGroup = currentDashboard . find (
61- ( group ) => group . title === groupTitle ,
62- ) ;
63- const sourceIndex = sourceData . index as number ;
64-
65- reorderSections ( sourceGroup , targetGroup , sourceIndex ) ;
42+ reorderSections ( sourceTitle , destinationTitle , sourceIndex ) ;
6643 }
6744 } ,
6845 } ) ;
6946
47+ // The groups are looked up inside the functional updater, so it always
48+ // sees the freshest state — no ref mirror, no re-subscription on every
49+ // dashboard change, and no window where a drop reads a stale dashboard.
7050 function reorderSections (
71- sourceGroup : DashboardGroup | undefined ,
72- destinationGroup : DashboardGroup | undefined ,
51+ sourceTitle : string ,
52+ destinationTitle : string ,
7353 sourceIndex : number ,
7454 destinationIndex : number | null = null ,
7555 ) {
76- if ( sourceGroup && destinationGroup ) {
77- if (
78- sourceGroup . title === destinationGroup . title &&
79- destinationIndex &&
80- sourceIndex < destinationIndex
81- ) {
82- destinationIndex -= 1 ;
56+ setUserDashboard ( ( groups ) => {
57+ const sourceGroup = groups . find ( ( group ) => group . title === sourceTitle ) ;
58+ const destinationGroup = groups . find (
59+ ( group ) => group . title === destinationTitle ,
60+ ) ;
61+ if ( ! sourceGroup || ! destinationGroup ) return groups ;
62+
63+ const sameGroup = sourceGroup . title === destinationGroup . title ;
64+ // Copy into a local so the updater stays pure (React StrictMode may
65+ // invoke it twice in development).
66+ let destIndex = destinationIndex ;
67+ if ( sameGroup && destIndex && sourceIndex < destIndex ) {
68+ destIndex -= 1 ;
8369 }
84- if (
85- sourceGroup . title === destinationGroup . title &&
86- ( destinationIndex === null || sourceIndex === destinationIndex )
87- ) {
88- return ;
70+ if ( sameGroup && ( destIndex === null || sourceIndex === destIndex ) ) {
71+ return groups ; // Nothing to do
8972 }
9073
91- if ( sourceGroup . title === destinationGroup . title ) {
74+ if ( sameGroup ) {
9275 const sourceItems = [ ...sourceGroup . items ] ;
9376 const [ removed ] = sourceItems . splice ( sourceIndex , 1 ) ;
94- if ( destinationIndex === null ) {
95- destinationIndex = sourceItems . length ;
77+ if ( destIndex === null ) {
78+ destIndex = sourceItems . length ;
9679 }
97- sourceItems . splice ( destinationIndex , 0 , removed ) ;
98-
99- setUserDashboard ( ( groups ) =>
100- groups . map ( ( group ) =>
101- group . title === sourceGroup . title
102- ? { ...group , items : sourceItems }
103- : group ,
104- ) ,
80+ sourceItems . splice ( destIndex , 0 , removed ) ;
81+ return groups . map ( ( group ) =>
82+ group . title === sourceGroup . title
83+ ? { ...group , items : sourceItems }
84+ : group ,
10585 ) ;
106- } else {
107- const sourceItems = [ ...sourceGroup . items ] ;
108- const [ removed ] = sourceItems . splice ( sourceIndex , 1 ) ;
109- const destinationItems = [ ...destinationGroup . items ] ;
110- if ( destinationIndex === null ) {
111- destinationIndex = destinationItems . length ;
112- }
113- destinationItems . splice ( destinationIndex , 0 , removed ) ;
86+ }
11487
115- setUserDashboard ( ( groups ) =>
116- groups . map ( ( group ) =>
117- group . title === sourceGroup . title
118- ? { ...group , items : sourceItems }
119- : group . title === destinationGroup . title
120- ? { ...group , items : destinationItems }
121- : group ,
122- ) ,
123- ) ;
88+ const sourceItems = [ ...sourceGroup . items ] ;
89+ const [ removed ] = sourceItems . splice ( sourceIndex , 1 ) ;
90+ const destinationItems = [ ...destinationGroup . items ] ;
91+ if ( destIndex === null ) {
92+ destIndex = destinationItems . length ;
12493 }
125- }
94+ destinationItems . splice ( destIndex , 0 , removed ) ;
95+ return groups . map ( ( group ) =>
96+ group . title === sourceGroup . title
97+ ? { ...group , items : sourceItems }
98+ : group . title === destinationGroup . title
99+ ? { ...group , items : destinationItems }
100+ : group ,
101+ ) ;
102+ } ) ;
126103 }
127104 } , [ setUserDashboard ] ) ;
128105}
0 commit comments