React ile Sıralanabilir Tablolar Oluşturma
Yayınlanan: 2022-03-10Tablo sıralama her zaman doğru olması oldukça zor bir konu olmuştur. İzlenmesi gereken çok sayıda etkileşim, yapılacak kapsamlı DOM mutasyonları ve hatta karmaşık sıralama algoritmaları var. Bu, düzeltilmesi zor olan zorluklardan sadece biri. Doğru?
Dış kütüphaneleri çekmek yerine, kendimiz bir şeyler yapmaya çalışalım. Bu makalede, React'te tablo verilerinizi sıralamak için yeniden kullanılabilir bir yol oluşturacağız. Her adımı ayrıntılı olarak inceleyeceğiz ve yol boyunca bir dizi faydalı teknik öğreneceğiz.
Temel React veya JavaScript sözdiziminden geçmeyeceğiz, ancak takip etmek için React konusunda uzman olmanıza gerek yok.
React ile Tablo Oluşturma
Öncelikle örnek bir tablo bileşeni oluşturalım. Bir dizi ürünü kabul edecek ve ürün başına bir satır listeleyen çok temel bir tablo çıkaracaktır.
function ProductTable(props) { const { products } = props; return ( <table> <caption>Our products</caption> <thead> <tr> <th>Name</th> <th>Price</th> <th>In Stock</th> </tr> </thead> <tbody> {products.map(product => ( <tr key={product.id}> <td>{product.name}</td> <td>{product.price}</td> <td>{product.stock}</td> </tr> ))} </tbody> </table> ); }
Burada bir dizi ürünü kabul ediyoruz ve bunları masamıza aktarıyoruz. Şu anda statik ve sıralanabilir değil, ancak şimdilik sorun değil.
Verileri Sıralama
Tüm beyaz tahta görüşmecilerine inanırsanız, yazılım geliştirmenin neredeyse tüm sıralama algoritmaları olduğunu düşünürdünüz. Neyse ki, burada hızlı bir sıralama veya kabarcıklı sıralamaya bakmayacağız.
JavaScript'te verileri sıralamak, yerleşik dizi işlevi sort()
sayesinde oldukça basittir. Fazladan bir argüman olmadan sayı ve karakter dizilerini sıralar:
const array = ['mozzarella', 'gouda', 'cheddar']; array.sort(); console.log(array); // ['cheddar', 'gouda', 'mozzarella']
Biraz daha akıllı bir şey istiyorsanız, ona bir sıralama işlevi verebilirsiniz. Bu işleve, listede argüman olarak iki öğe verilir ve verdiğiniz karara göre birini diğerinin önüne yerleştirir.
Aldığımız verileri ada göre alfabetik olarak sıralayarak başlayalım.
function ProductTable(props) { const { products } = props; let sortedProducts = [...products]; sortedProducts.sort((a, b) => { if (a.name < b.name) { return -1; } if (a.name > b.name) { return 1; } return 0; }); return ( <Table> {/* as before */} </Table> ); }
Peki burada neler oluyor? İlk olarak, istediğimiz gibi değiştirebileceğimiz ve değiştirebileceğimiz ürün desteğinin bir kopyasını oluşturuyoruz. Bunu yapmamız gerekiyor çünkü Array.prototype.sort
işlevi yeni bir sıralanmış kopya döndürmek yerine orijinal diziyi değiştiriyor.
Ardından sortedProducts.sort
çağırır ve ona bir sorting
işlevi iletiriz. İlk argüman a
name
özelliğinin ikinci argüman b
önce olup olmadığını kontrol ederiz ve eğer öyleyse, negatif bir değer döndürürüz. Bu, a'nın listede b
önce a
gerektiğini gösterir. İlk argümanın adı ikinci argümanın adından sonraysa, b
a'nın önüne koymamız gerektiğini belirten pozitif a
sayı döndürürüz. İkisi eşitse (yani her ikisi de aynı ada sahipse), sırayı korumak için 0
döndürürüz.
Masamızı Sıralanabilir Hale Getirmek
Artık tablonun ada göre sıralandığından emin olabiliriz - ancak sıralama düzenini kendimiz nasıl değiştirebiliriz?
Hangi alana göre sıraladığımızı değiştirmek için şu anda sıralanmış alanı hatırlamamız gerekiyor. Bunu useState
kancasıyla yapacağız.
Kanca, durumu yönetmek ve yan etkileri tetiklemek gibi React'in bazı temel işlevlerine "bağlanmamıza" izin veren özel bir işlev türüdür. Bu özel kanca, bileşenimizde bir parça iç durumu korumamıza ve istersek değiştirmemize izin verir. Bu ekleyeceğimiz şey:
const [sortedField, setSortedField] = React.useState(null);
Hiçbir şeyi sıralamadan başlıyoruz. Ardından, hangi alana göre sıralamak istediğimizi değiştirmenin bir yolunu içerecek şekilde tablo başlıklarını değiştirelim.
const ProductsTable = (props) => { const { products } = props; const [sortedField, setSortedField] = React.useState(null); return ( <table> <thead> <tr> <th> <button type="button" onClick={() => setSortedField('name')}> Name </button> </th> <th> <button type="button" onClick={() => setSortedField('price')}> Price </button> </th> <th> <button type="button" onClick={() => setSortedField('stock')}> In Stock </button> </th> </tr> </thead> {/* As before */} </table> ); };
Artık bir tablo başlığına her tıkladığımızda, sıralamak istediğimiz alanı güncelliyoruz. Temiz-o!
Henüz gerçek bir sıralama yapmıyoruz, o yüzden düzeltelim. Daha önceki sıralama algoritmasını hatırlıyor musunuz? İşte burada, alan adlarımızdan herhangi biriyle çalışmak için biraz değiştirildi.
const ProductsTable = (props) => { const { products } = props; const [sortedField, setSortedField] = React.useState(null); let sortedProducts = [...products]; if (sortedField !== null) { sortedProducts.sort((a, b) => { if (a[sortedField] < b[sortedField]) { return -1; } if (a[sortedField] > b[sortedField]) { return 1; } return 0; }); } return ( <table>
Önce bir alan seçtiğimizden emin oluyoruz ve eğer öyleyse, ürünleri o alana göre sıralıyoruz.
Artan vs Azalan
Görmek istediğimiz bir sonraki özellik, artan ve azalan düzen arasında geçiş yapmanın bir yoludur. Tablo başlığına bir kez daha tıklayarak artan ve azalan düzen arasında geçiş yapacağız.
Bunu uygulamak için, ikinci bir durum parçası tanıtmamız gerekecek - sıralama düzeni. Hem alan adını hem de yönünü korumak için mevcut sortedField
durum değişkenimizi yeniden değerlendireceğiz. Bu durum değişkeni, bir dize içermek yerine, anahtarı (alan adı) ve yönü olan bir nesne içerecektir. Biraz daha net olması için sortConfig
olarak yeniden adlandıracağız.
İşte yeni sıralama işlevi:
sortedProducts.sort((a, b) => { if (a[sortConfig.key] < b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (a[sortConfig.key] > b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0; });
Şimdi yön 'yükseliyor' ise, daha önce yaptığımız gibi yapacağız. Değilse, bize azalan sıralama vererek tersini yapacağız.
Sırada, alan adını kabul edecek ve durumu buna göre güncelleyecek olan requestSort
adlı yeni bir işlev oluşturacağız.
const requestSort = key => { let direction = 'ascending'; if (sortConfig.key === key && sortConfig.direction === 'ascending') { direction = 'descending'; } setSortConfig({ key, direction }); }
Bu yeni işlevi kullanmak için tıklama işleyicilerimizi de değiştirmemiz gerekecek!
return ( <table> <thead> <tr> <th> <button type="button" onClick={() => requestSort('name')}> Name </button> </th> <th> <button type="button" onClick={() => requestSort('price')}> Price </button> </th> <th> <button type="button" onClick={() => requestSort('stock')}> In Stock </button> </th> </tr> </thead> {/* as before */} </table> );
Şimdi oldukça eksiksiz görünmeye başlıyoruz, ancak hala yapılacak büyük bir şey var. Verilerimizi yalnızca ihtiyacımız olduğunda sıraladığımızdan emin olmalıyız. Şu anda, her işlemede tüm verilerimizi sıralıyoruz, bu da ileride her türlü performans sorununa yol açacaktır. Bunun yerine, tüm yavaş parçaları not almak için yerleşik useMemo
kancasını kullanalım!
const ProductsTable = (props) => { const { products } = props; const [sortConfig, setSortConfig] = React.useState(null); React.useMemo(() => { let sortedProducts = [...products]; if (sortedField !== null) { sortedProducts.sort((a, b) => { if (a[sortConfig.key] < b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (a[sortConfig.key] > b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0; }); } return sortedProducts; }, [products, sortConfig]);
Daha önce görmediyseniz, useMemo
pahalı hesaplamaları önbelleğe almanın veya not almanın bir yoludur. Aynı girdi verildiğinde, herhangi bir nedenle bileşenimizi yeniden oluşturursak, ürünleri iki kez sıralamak zorunda kalmaz. Ürünlerimiz değiştiğinde veya sıraladığımız alan veya yön değiştiğinde yeni bir sıralamayı tetiklemek istediğimizi unutmayın.
Kodumuzu bu işleve sarmak, tablo sıralamamız için büyük performans etkilerine sahip olacaktır!
Her Şeyi Yeniden Kullanılabilir Hale Getirmek
Kancalarla ilgili en iyi şeylerden biri, mantığı yeniden kullanılabilir hale getirmenin ne kadar kolay olduğudur. Muhtemelen uygulamanız boyunca her tür tabloyu sıralayacaksınız ve aynı şeyleri yeniden uygulamak zorunda kalacaksınız, kulağa bir sürükleme gibi geliyor.
React, özel kancalar adı verilen bu özelliğe sahiptir. Kulağa hoş geliyorlar, ancak bunların tümü, içlerindeki diğer kancaları kullanan normal işlevlerdir. Özel bir kancada yer alacak kodumuzu yeniden düzenleyelim, böylece onu her yerde kullanabiliriz!
const useSortableData = (items, config = null) => { const [sortConfig, setSortConfig] = React.useState(config); const sortedItems = React.useMemo(() => { let sortableItems = [...items]; if (sortConfig !== null) { sortableItems.sort((a, b) => { if (a[sortConfig.key] < b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? -1 : 1; } if (a[sortConfig.key] > b[sortConfig.key]) { return sortConfig.direction === 'ascending' ? 1 : -1; } return 0; }); } return sortableItems; }, [items, sortConfig]); const requestSort = key => { let direction = 'ascending'; if (sortConfig && sortConfig.key === key && sortConfig.direction === 'ascending') { direction = 'descending'; } setSortConfig({ key, direction }); } return { items: sortedItems, requestSort }; }
Bu, bir miktar yeniden adlandırma ile birlikte önceki kodumuzdan kopyala ve yapıştır işlemidir. useSortableData
, öğeleri ve isteğe bağlı bir ilk sıralama durumunu kabul eder. Sıralanmış öğelere sahip bir nesne ve öğeleri yeniden sıralamak için bir işlev döndürür.
Tablo kodumuz şimdi şöyle görünür:
const ProductsTable = (props) => { const { products } = props; const { items, requestSort } = useSortableData(products); return ( <table>{/* ... */}</table> ); };
Son Dokunuş
Küçük bir parça eksik - tablonun nasıl sıralandığını göstermenin bir yolu. Bunu tasarımımızda belirtmek için, dahili durumu da döndürmemiz gerekiyor - sortConfig
. Bunu da döndürelim ve tablo başlıklarımıza uygulayabileceğimiz stiller oluşturmak için kullanalım!
const ProductTable = (props) => { const { items, requestSort, sortConfig } = useSortableData(props.products); const getClassNamesFor = (name) => { if (!sortConfig) { return; } return sortConfig.key === name ? sortConfig.direction : undefined; }; return ( <table> <caption>Products</caption> <thead> <tr> <th> <button type="button" onClick={() => requestSort('name')} className={getClassNamesFor('name')} > Name </button> </th> {/* … */} </tr> </thead> {/* … */} </table> ); };
Ve bununla işimiz bitti!
Toplama
Sonuç olarak, kendi tablo sıralama algoritmanızı oluşturmak imkansız bir başarı değildi. Durumumuzu modellemenin bir yolunu bulduk, genel bir sıralama işlevi yazdık ve sıralama tercihlerimizi güncellemenin bir yolunu yazdık. Her şeyin performans gösterdiğinden emin olduk ve hepsini özel bir kancaya dönüştürdük. Son olarak, kullanıcıya sıralama düzenini belirtmenin bir yolunu sağladık.
Bu CodeSandbox'ta tablonun bir demosunu görebilirsiniz: