React İle Sürükle Bırak Özellikli Bir Liste Bileşeni Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce bilgisayarınızda Node.js'in güncel bir sürümünün (LTS) yüklü olduğundan emin olmalısınız. React projenizi Vite kullanarak hızlıca başlatabilir ve gerekli bağımlılıkları yükleyebilirsiniz. Bu örnekte, sürükle bırak işlemlerini yönetmek için @dnd-kit/core, @dnd-kit/sortable ve @dnd-kit/utilities paketlerini kullanacağız.

npx create-vite@latest surukle-birak-projesi --template react
cd surukle-birak-projesi
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities

Yukarıdaki komutlar, projenizi kuracak ve sürükle bırak işlemleri için gerekli olan modern kütüphaneleri çalışma ortamınıza dahil edecektir. Bu paketler, dokunmatik ekran desteği ve klavye navigasyonu gibi erişilebilirlik özelliklerini de beraberinde getirir.

Sürükle Bırak Mantığı ve Bileşen Yapısı

Sürükle bırak mantığı, React'te "kontrollü bileşen" prensibiyle çalışır. Liste veriniz bir state (durum) içerisinde tutulur ve kullanıcı bir öğeyi taşıdığında, bu state güncellenerek bileşenin yeniden render edilmesi sağlanır. Aşağıdaki kod bloğunda, temel liste verimizi tanımlıyoruz.

import { useState } from 'react';

const [items, setItems] = useState([
  { id: '1', title: 'Görev 1' },
  { id: '2', title: 'Görev 2' },
  { id: '3', title: 'Görev 3' },
]);

Burada items dizisi, sürükleme işlemi sırasında yer değiştirecek olan verilerimizi temsil eder. Her öğenin benzersiz bir id değerine sahip olması, dnd-kit kütüphanesinin öğeleri doğru şekilde tanımlayabilmesi için zorunludur.

Sıralanabilir Liste Bileşenini Oluşturma

Sıralanabilir bir liste oluşturmak için @dnd-kit/sortable kütüphanesinden gelen useSortable hook'unu kullanırız. Bu hook, bir bileşeni sürüklenebilir ve bırakılabilir hale getiren gerekli özellikleri (attributes) ve referansları (listeners) sağlar.

import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

export function SortableItem({ id, title }) {
  const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    padding: '10px',
    border: '1px solid #ccc',
    marginBottom: '5px',
    backgroundColor: 'white'
  };

  return (
    
{title}
); }

Bu bileşen, sürükleme sırasında oluşan transform değerini CSS'e dönüştürerek akıcı bir animasyon sağlar. setNodeRef, kütüphanenin DOM üzerindeki konumu takip etmesine yardımcı olur.

Sürükleme İşlemini Yönetme (DndContext)

Tüm listenin sürükleme olaylarını yönetmek için DndContext sarmalayıcısına ihtiyacımız vardır. Bu sarmalayıcı, sürükleme başladığında, sürükleme sırasında ve bırakma gerçekleştiğinde tetiklenen olayları (event) yakalar.

import { DndContext, closestCenter } from '@dnd-kit/core';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';

function App() {
  return (
    
      
        {items.map(item => )}
      
    
  );
}

collisionDetection parametresi, sürüklenen öğenin bırakıldığı noktayı nasıl hesaplayacağımızı belirler. closestCenter, öğenin merkezine en yakın olan hedefi algılamak için en ideal yöntemdir.

Öğelerin Yer Değiştirme Mantığı

Sürükleme işlemi bittiğinde, handleDragEnd fonksiyonu tetiklenir. Bu fonksiyon, dizideki öğelerin yerini değiştirmekten sorumludur. arrayMove yardımcı fonksiyonu, bu işlemi güvenli bir şekilde gerçekleştirmemizi sağlar.

import { arrayMove } from '@dnd-kit/sortable';

function handleDragEnd(event) {
  const { active, over } = event;

  if (active.id !== over.id) {
    setItems((items) => {
      const oldIndex = items.findIndex((i) => i.id === active.id);
      const newIndex = items.findIndex((i) => i.id === over.id);
      return arrayMove(items, oldIndex, newIndex);
    });
  }
}

Bu kod bloğu, sürüklenen öğenin (active) ve bırakılan yerin (over) id'lerini karşılaştırır. Eğer öğe farklı bir konuma bırakıldıysa, arrayMove ile diziyi güncelleriz.

Yöntemlerin Karşılaştırılması

Yöntem Avantajı Dezavantajı
dnd-kit Modern, erişilebilir, modüler Öğrenme eğrisi mevcut
react-beautiful-dnd Popüler, görsel olarak zengin Bakımı yavaşladı, güncel değil
HTML5 Drag/Drop API Dış kütüphane gerektirmez Erişilebilirlik ve mobil desteği zayıf

Güvenlik Uyarısı: Kullanıcıdan gelen veriyle liste sıralamasını güncellerken, sunucu tarafında her zaman yetkilendirme kontrolü yapın. Sürükle bırak işlemleri istemci tarafında gerçekleşse de, sıralama verisini veritabanına kaydederken XSS (Cross-Site Scripting) saldırılarına karşı verileri sanitize etmeyi unutmayın.

Sıkça Sorulan Sorular

Sürükle bırak sırasında animasyonları nasıl daha akıcı yapabilirim?

@dnd-kit/sortable içerisinde yer alan SortableContext, varsayılan olarak animasyonları destekler. transition özelliğini useSortable hook'undan alıp CSS'e uyguladığınızdan emin olun.

Bu yöntem mobil cihazlarda çalışır mı?

Evet, dnd-kit kütüphanesi dokunmatik ekranlar için yerleşik desteğe sahiptir ve herhangi bir ek yapılandırma gerektirmez.

İç içe geçmiş listeler oluşturabilir miyim?

Evet, ancak bu durumda her liste için ayrı bir DndContext ve SortableContext yapısı kurmanız gerekecektir.

Performans sorunlarını nasıl engellerim?

Bileşenlerinizi React.memo ile sarmalayarak, sadece değişen öğelerin yeniden render edilmesini sağlayabilirsiniz.

Sürükleme sırasında öğenin görselini nasıl özelleştirebilirim?

DragOverlay bileşenini kullanarak, sürüklenen öğenin o anki görüntüsünü tamamen özelleştirebilir ve farklı bir stil verebilirsiniz.

Sıralama Değişikliklerini Veritabanına Kaydetme (Auto-Save)

Kullanıcı bir öğeyi sürükleyip bıraktığında, bu değişikliğin kalıcı olması için yeni sıralamayı bir API uç noktasına göndermeniz gerekir. dnd-kit içerisinde onDragEnd olayını tetikleyerek güncel diziyi sunucuya iletebilirsiniz. İşte bu süreci yönetmek için basit bir örnek:

const handleDragEnd = async (event) => {
  const { active, over } = event;

  if (active.id !== over.id) {
    const oldIndex = items.findIndex((i) => i.id === active.id);
    const newIndex = items.findIndex((i) => i.id === over.id);
    const newItems = arrayMove(items, oldIndex, newIndex);
    
    setItems(newItems);

    // Veritabanı güncelleme işlemi
    try {
      await fetch('/api/update-order', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ items: newItems }),
      });
    } catch (error) {
      console.error("Sıralama kaydedilemedi:", error);
    }
  }
};

Bu yapıda dikkat etmeniz gereken en önemli nokta, ağ gecikmelerini (latency) yönetmektir. Eğer çok sık sürükleme işlemi yapılıyorsa, debounce fonksiyonu kullanarak API isteklerini sınırlandırmanız sunucu yükünü ciddi oranda azaltacaktır.

Sürükle Bırak Bileşenleri İçin Birim Testleri

Karmaşık arayüz bileşenlerinde sürükle bırak fonksiyonlarının doğruluğunu test etmek, uygulamanın kararlılığı için kritiktir. @testing-library/react ve user-event kütüphanelerini kullanarak sürükleme simülasyonu yapabilirsiniz.

Test senaryonuzda, öğenin başlangıç pozisyonunu ve sürükleme sonrası yeni pozisyonunu kontrol etmeniz gerekir. Ancak dnd-kit gibi kütüphaneler DOM tabanlı hesaplamalar yaptığı için, test ortamında getBoundingClientRect gibi metodların mock (taklit) edilmesi gerekebilir.

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import SortableList from './SortableList';

test('öğeler sürüklendiğinde sıralama değişmeli', async () => {
  render();
  
  const items = screen.getAllByRole('listitem');
  const firstItem = items[0];
  const secondItem = items[1];

  // Sürükle bırak simülasyonu
  await userEvent.pointer([
    { keys: '[MouseLeft>]', target: firstItem },
    { coords: { x: 0, y: 500 }, target: secondItem },
    { keys: '[/MouseLeft]' }
  ]);

  const updatedItems = screen.getAllByRole('listitem');
  expect(updatedItems[0]).toHaveTextContent('İkinci Öğe');
});

Testlerde Dikkat Edilmesi Gerekenler

  • Koordinat Hesaplama: Test ortamında fiziksel bir ekran olmadığı için getBoundingClientRect değerlerini manuel olarak mock'lamanız gerekebilir.
  • Asenkron İşlemler: dnd-kit animasyonları asenkron çalışır; testlerinizde findBy sorgularını kullanarak DOM'un güncellenmesini bekleyin.
  • Erişilebilirlik: Sürükle bırak listenizin klavye ile yönetilebilir olduğunu tabindex ve aria-describedby özelliklerini test ederek doğrulayın.

Sonuç

React ile sürükle bırak özellikli bir liste bileşeni oluşturmak, doğru kütüphane seçimi ve state yönetimi ile oldukça verimli bir süreçtir. Bu rehberde öğrendiğiniz dnd-kit kullanımı, projenize modern bir kullanıcı deneyimi kazandıracaktır. Bir sonraki adım olarak, sürükleme bittikten sonra yeni sıralamayı bir API aracılığıyla sunucuya kaydeden bir "Auto-Save" (otomatik kaydetme) mekanizması eklemeyi deneyebilirsiniz.

Yazılım geliştirmede güvenlik ve performans, kullanıcı deneyiminin temelidir. Kodunuzu her zaman güncel tutun ve kütüphane dokümantasyonlarını periyodik olarak kontrol ederek en iyi pratikleri uygulamaya devam edin.

Bu yazıya tepkinizi paylaşın:
Burak Çelik

Doğa sporları, kampçılık ve açık hava aktiviteleri konularında uzmanlaşmış bir rehber yazarıyım. Yeni başlayanlar için güvenli ve keyifli bir deneyim sunacak, teknik bilgisi yüksek içerikler üretiyorum.

Yorumlar (0)

Yorum Yaz