3

When I drop an element to resort, there's a slight transition issue where the items goes back to its' original position & then transitions. Below I added a link to video & code.

Really, I don't want any transition delay, etc. I feel like it could be the remapping of the array, but i've tested it by optimizing as much as possible & still getting the same issue. It works fine without the transition property on there, but would like it to make things feel smooth.

Link to video --> Link to video

import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import React from 'react';
import Icon from '../../../../../../../common/components/icon/Icon';

interface Props {
   id: number;
   children: React.ReactNode;
}

const SortableItem = ({ id, children }: Props) => {
   const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: id });

   return (
      <div
         ref={setNodeRef}
         style={{
            transform: CSS.Transform.toString(transform),
            transition,
         }}>
         <div className='grid grid-cols-[60px_auto] items-center w-full h-full relative'>
            {/* Make this the icon grabble */}
            <div
               className='flex items-center w-full h-full cursor-grab'
               data-grab={true}
               {...attributes}
               {...listeners}>
               <Icon name='ArrowsUpDownLeftRight' width={20} height={20} />
            </div>
            {children}
         </div>
      </div>
   );
};

export default SortableItem;
'use client';
import React, { useState } from 'react';
import { ListenService } from '../SmartLinkClient';
import Icon from '../../../../../../common/components/icon/Icon';
import Input from '../../../../../../common/components/input/Input';
import ToggleSwitch from '../../../../../../common/components/toggle-switch/ToggleSwitch';
import Hr from '../../../../../../common/components/hr/Hr';
import { DndContext, PointerSensor, useSensor, useSensors, closestCenter } from '@dnd-kit/core';
import { SortableContext, arrayMove, verticalListSortingStrategy } from '@dnd-kit/sortable';
import SortableItem from './(edit-services-partials)/SortableItem';

interface Props {
   servicesConfig: ListenService[];
   handleServiceShowToggle: (e: any, elementToChange: ListenService) => void;
   handleServiceDragEnd: (active: any, over: any) => void;
}

const EditServices = ({ servicesConfig, handleServiceShowToggle, handleServiceDragEnd }: Props) => {

   const sensors = useSensors(useSensor(PointerSensor));
   return (
      <div className='select-services'>
         <div className='flex flex-col gap-y-4'>
            <DndContext
               sensors={sensors}
               collisionDetection={closestCenter}
               onDragEnd={({ active, over }) => {
                  handleServiceDragEnd(active, over);
               }}>
               <SortableContext
                  items={servicesConfig.map((service: ListenService) => service.id)}
                  strategy={verticalListSortingStrategy}>
                  {/* Components that use the useSortable hook */}
                  {servicesConfig.map((service: ListenService, i: number) => (
                     <SortableItem key={i} id={service.id}>
                        <div className='grid grid-cols-[minmax(160px,180px)_minmax(200px,auto)_100px] items-center'>
                           <div className='grid grid-cols-[24px_auto] items-center gap-x-3 dark:text-stone-100 text-stone-800'>
                              <Icon name={service.iconName} width={24} height={24} color={service.color} />
                              <span className='text-[16px]'>{service.name}</span>
                           </div>
                           <Input
                              placeholder='We could not find a valid URL, but you can enter your own.'
                              type={'url'}
                           />
                           <div className='justify-self-end -mt-2 flex flex-row items-center gap-x-2'>
                              <span className='text-[11px] text-stone-600 dark:text-stone-300'>Show</span>
                              <ToggleSwitch
                                 toggled={service.show}
                                 handleToggled={(e: any) => {
                                    handleServiceShowToggle(e, service);
                                 }}
                              />
                           </div>
                        </div>
                        {i !== servicesConfig.length - 1 && (
                           <span className='col-span-2'>
                              <Hr />
                           </span>
                        )}
                     </SortableItem>
                  ))}
               </SortableContext>
            </DndContext>
         </div>
      </div>
   );
};

export default EditServices;
Matthew
  • 41
  • 3

1 Answers1

1

I ended up figuring it out. The key on the <SortableItem /> component must be the same as the id on the <SortableItem /> component. Weird, but makes sense.

Previous: ex) <SortableItem key={i} id={service.id} />

Current (Solution): ex) <SortableItem key={service.id} id={service.id} />

Matthew
  • 41
  • 3
  • Upon testing I found that `key` should be a unique random id. Using an array index as a key is an anti-pattern, especially for a sortable list. You can read it here https://reactjs.org/docs/lists-and-keys.html#keys – Akash Kumar Seth Jan 31 '23 at 22:26
  • were you able to solve the issue? I've tried what you mentioned and the issue still happens sometimes. Also, sometimes 2 items are dragged together. – jonatasdaniel Aug 02 '23 at 02:29