Thursday

19-06-2025 Vol 19

Tipado genérico en acción: implementando una tabla en React con TypeScript

Tipado Generik dalam Aksi: Implementasi Tabel Fleksibel di React dengan TypeScript

React dan TypeScript adalah kekuatan dinamis dalam pengembangan web modern. TypeScript membawa keamanan tipe yang sangat dibutuhkan ke dunia JavaScript, membantu kita menangkap kesalahan sebelum dijalankan. React, di sisi lain, menyediakan cara deklaratif dan berbasis komponen untuk membangun antarmuka pengguna yang interaktif. Ketika keduanya digabungkan, kita dapat membangun aplikasi yang kuat, mudah dipelihara, dan aman dari kesalahan.

Salah satu pola umum dalam pengembangan web adalah menampilkan data tabular. Tabel adalah cara yang efisien dan mudah dibaca untuk menyajikan informasi terstruktur kepada pengguna. Dalam artikel ini, kita akan menjelajahi bagaimana memanfaatkan tipado generik TypeScript untuk membuat komponen tabel React yang sangat fleksibel dan dapat digunakan kembali.

Mengapa Menggunakan Tipado Generik untuk Komponen Tabel?

Sebelum kita menyelami kode, mari kita bahas mengapa tipado generik sangat bermanfaat dalam konteks komponen tabel:

  1. Keamanan Tipe: Generik memungkinkan kita untuk mendefinisikan tipe data yang digunakan oleh komponen tabel kita secara dinamis. Ini berarti bahwa kita dapat memastikan bahwa data yang kita tampilkan sesuai dengan struktur yang kita harapkan, mencegah kesalahan runtime.
  2. Penggunaan Ulang: Dengan generik, kita dapat membuat satu komponen tabel yang dapat digunakan untuk menampilkan berbagai jenis data. Kita tidak perlu menulis komponen tabel terpisah untuk setiap jenis data yang berbeda.
  3. Fleksibilitas: Generik memberi kita fleksibilitas untuk menyesuaikan komponen tabel kita sesuai dengan kebutuhan spesifik kita. Kita dapat menentukan properti dan fungsi yang berbeda untuk berbagai jenis data.
  4. Kode yang Lebih Bersih: Menggunakan generik dapat menghasilkan kode yang lebih bersih dan mudah dibaca. Ini karena kita tidak perlu menggunakan tipe any atau melakukan casting tipe secara manual.

Kerangka Artikel: Dari Konsep Hingga Implementasi

Berikut adalah kerangka yang akan kita ikuti untuk membangun komponen tabel generik kita:

  1. Definisikan Tipe Data: Tentukan interface TypeScript yang mewakili struktur data yang akan kita tampilkan dalam tabel.
  2. Buat Komponen Tabel Dasar: Bangun komponen React dasar yang menerima data dan konfigurasi kolom sebagai properti.
  3. Implementasikan Tipado Generik: Gunakan generik TypeScript untuk membuat komponen tabel lebih fleksibel dan aman tipe.
  4. Tambahkan Konfigurasi Kolom: Izinkan pengembang untuk mengkonfigurasi kolom yang akan ditampilkan, termasuk judul, bidang data, dan format tampilan.
  5. Implementasikan Sortir: Tambahkan fungsionalitas untuk mengurutkan data berdasarkan kolom yang berbeda.
  6. Implementasikan Pencarian: Tambahkan fungsionalitas untuk mencari data dalam tabel.
  7. Penomoran Halaman: Implementasikan penomoran halaman untuk menangani set data yang besar.
  8. Styling: Terapkan gaya dasar pada tabel untuk membuatnya terlihat lebih menarik.
  9. Contoh Penggunaan: Berikan contoh konkret bagaimana menggunakan komponen tabel dengan data yang berbeda.
  10. Kesimpulan: Rangkum manfaat menggunakan tipado generik untuk komponen tabel React dan berikan saran untuk pengembangan lebih lanjut.

1. Definisikan Tipe Data

Langkah pertama adalah mendefinisikan interface TypeScript yang mewakili struktur data yang akan kita tampilkan dalam tabel. Misalnya, jika kita ingin menampilkan daftar pengguna, kita dapat mendefinisikan interface seperti ini:

“`typescript
interface User {
id: number;
firstName: string;
lastName: string;
email: string;
age: number;
}
“`

Interface ini mendefinisikan tipe data untuk setiap properti dalam objek User. Kita akan menggunakan interface ini untuk menipe data yang kita tampilkan dalam tabel.

2. Buat Komponen Tabel Dasar

Selanjutnya, kita akan membuat komponen React dasar yang menerima data dan konfigurasi kolom sebagai properti. Komponen ini akan bertanggung jawab untuk merender tabel HTML.

“`typescript
import React from ‘react’;

interface Props {
data: any[];
columns: {
Header: string;
accessor: string;
}[];
}

const BasicTable: React.FC = ({ data, columns }) => {
return (

{columns.map((column) => (

))}

{data.map((row, index) => (

{columns.map((column) => (

))}

))}

{column.Header}
{row[column.accessor]}

);
};

export default BasicTable;
“`

Komponen BasicTable menerima dua properti:

  • data: Array data yang akan ditampilkan dalam tabel. Saat ini bertipe any[], yang akan kita ubah nanti.
  • columns: Array objek yang mendefinisikan kolom yang akan ditampilkan dalam tabel. Setiap objek kolom memiliki properti Header (judul kolom) dan accessor (bidang data yang akan ditampilkan).

Komponen ini merender tabel HTML dasar dengan header dan baris data. Header dibuat menggunakan properti Header dari setiap objek kolom, dan data sel diisi menggunakan properti accessor.

3. Implementasikan Tipado Generik

Sekarang kita akan mengimplementasikan tipado generik untuk membuat komponen tabel lebih fleksibel dan aman tipe. Kita akan mengubah tipe data data dari any[] menjadi tipe generik T[], di mana T adalah tipe data yang akan kita tampilkan dalam tabel.

“`typescript
import React from ‘react’;

interface Props {
data: T[];
columns: {
Header: string;
accessor: keyof T; // Memastikan accessor sesuai dengan key pada T
}[];
}

const GenericTable = ({ data, columns }: Props) => {
return (

{columns.map((column) => (

))}

{data.map((row, index) => (

{columns.map((column) => (

))}

))}

{column.Header}
{String(row[column.accessor])}

);
};

export default GenericTable;
“`

Beberapa perubahan penting telah dilakukan:

  • Tipe Generik T: Kita telah memperkenalkan tipe generik T untuk mewakili tipe data yang akan kita tampilkan dalam tabel.
  • Props: Interface Props sekarang menggunakan tipe generik T untuk mendefinisikan tipe data untuk properti data. data sekarang bertipe T[], yang berarti array dari tipe T.
  • keyof T untuk accessor: Kita telah mengubah tipe accessor menjadi keyof T. Ini memastikan bahwa nilai accessor harus berupa kunci yang valid pada tipe T. Ini meningkatkan keamanan tipe dan mencegah kesalahan di mana kita mencoba mengakses bidang yang tidak ada.
  • Batasan Tipe T extends {}: Kita telah menambahkan batasan T extends {}. Ini memastikan bahwa T adalah tipe objek.
  • Konversi ke String: Kita melakukan konversi explicit menjadi string dengan `String(row[column.accessor])` untuk mengatasi kemungkinan error dari penggunaan tipe data non-string.

Dengan perubahan ini, komponen GenericTable kita sekarang menggunakan tipado generik. Ini berarti bahwa kita dapat menggunakan komponen yang sama untuk menampilkan berbagai jenis data tanpa harus menulis komponen terpisah untuk setiap jenis data.

4. Tambahkan Konfigurasi Kolom yang Lebih Fleksibel

Sekarang mari tingkatkan konfigurasi kolom untuk memberikan lebih banyak fleksibilitas. Kita akan memungkinkan pengembang untuk menentukan format tampilan untuk setiap kolom.

“`typescript
import React from ‘react’;

interface ColumnConfig {
Header: string;
accessor: keyof T;
Cell?: (row: T) => React.ReactNode; // Opsi untuk memformat tampilan sel
}

interface Props {
data: T[];
columns: ColumnConfig[];
}

const FlexibleTable = ({ data, columns }: Props) => {
return (

{columns.map((column) => (

))}

{data.map((row, index) => (

{columns.map((column) => (

))}

))}

{column.Header}
{column.Cell ? column.Cell(row) : String(row[column.accessor])}

);
};

export default FlexibleTable;
“`

Kita telah menambahkan properti Cell opsional ke interface ColumnConfig. Properti Cell adalah fungsi yang menerima objek data baris sebagai argumen dan mengembalikan node React yang akan dirender di sel. Jika properti Cell tidak ditentukan, kita akan menampilkan nilai data mentah, seperti sebelumnya.

Berikut adalah contoh bagaimana kita dapat menggunakan properti Cell untuk memformat tampilan kolom usia:

“`typescript
const columns: ColumnConfig[] = [
{
Header: ‘ID’,
accessor: ‘id’,
},
{
Header: ‘Nama Depan’,
accessor: ‘firstName’,
},
{
Header: ‘Nama Belakang’,
accessor: ‘lastName’,
},
{
Header: ‘Email’,
accessor: ’email’,
},
{
Header: ‘Usia’,
accessor: ‘age’,
Cell: (user) => {user.age} tahun, // Memformat usia
},
];
“`

Dalam contoh ini, kita telah menentukan fungsi Cell untuk kolom usia. Fungsi ini mengembalikan elemen span yang menampilkan usia diikuti oleh teks “tahun”.

5. Implementasikan Sortir

Menambahkan fungsionalitas sortir ke komponen tabel kita akan membuatnya lebih interaktif dan berguna. Kita akan memungkinkan pengguna untuk mengurutkan data berdasarkan kolom yang berbeda dengan mengklik header kolom.

“`typescript
import React, { useState, useCallback } from ‘react’;

interface ColumnConfig {
Header: string;
accessor: keyof T;
Cell?: (row: T) => React.ReactNode;
sortable?: boolean; // Menambahkan properti sortable
}

interface Props {
data: T[];
columns: ColumnConfig[];
}

const SortableTable = ({ data, columns }: Props) => {
const [sortBy, setSortBy] = useState(null);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(‘asc’);

const handleSort = useCallback(
(accessor: keyof T) => {
if (accessor === sortBy) {
setSortDirection(sortDirection === ‘asc’ ? ‘desc’ : ‘asc’);
} else {
setSortBy(accessor);
setSortDirection(‘asc’);
}
},
[sortBy, sortDirection]
);

const sortedData = React.useMemo(() => {
if (!sortBy) {
return data;
}

const column = columns.find((col) => col.accessor === sortBy);
if (!column || !column.sortable) {
return data;
}

const sorted = […data].sort((a, b) => {
const valueA = a[sortBy];
const valueB = b[sortBy];

if (valueA === undefined || valueA === null) return -1;
if (valueB === undefined || valueB === null) return 1;

if (typeof valueA === ‘string’ && typeof valueB === ‘string’) {
return sortDirection === ‘asc’ ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA);
}

if (typeof valueA === ‘number’ && typeof valueB === ‘number’) {
return sortDirection === ‘asc’ ? valueA – valueB : valueB – valueA;
}

return 0;
});

return sorted;
}, [data, sortBy, sortDirection, columns]);

return (

{columns.map((column) => (

))}

{sortedData.map((row, index) => (

{columns.map((column) => (

))}

))}

column.sortable ? handleSort(column.accessor) : undefined}
style={{ cursor: column.sortable ? ‘pointer’ : ‘default’ }}
>
{column.Header}
{sortBy === column.accessor && (
{sortDirection === ‘asc’ ? ‘ ▲’ : ‘ ▼’}
)}
{column.Cell ? column.Cell(row) : String(row[column.accessor])}

);
};

export default SortableTable;
“`

Beberapa perubahan penting telah dilakukan:

  • sortable Properti: Kita telah menambahkan properti sortable opsional ke interface ColumnConfig. Properti ini menentukan apakah kolom dapat diurutkan.
  • State sortBy dan sortDirection: Kita telah menambahkan state sortBy dan sortDirection untuk melacak kolom mana yang sedang diurutkan dan arah pengurutan.
  • handleSort Fungsi: Kita telah menambahkan fungsi handleSort yang dipanggil ketika header kolom diklik. Fungsi ini memperbarui state sortBy dan sortDirection.
  • sortedData Variabel: Kita telah menambahkan variabel sortedData yang menyimpan data yang telah diurutkan. Variabel ini menggunakan useMemo untuk menghitung data yang diurutkan hanya ketika data, sortBy, atau sortDirection berubah.
  • Pengaturan Tampilan: Tampilan diubah untuk menunjukkan arah pengurutan pada header yang diklik.

Sekarang, ketika pengguna mengklik header kolom, data akan diurutkan berdasarkan kolom itu. Jika pengguna mengklik header kolom lagi, arah pengurutan akan dibalik.

6. Implementasikan Pencarian

Selanjutnya, mari kita tambahkan fungsionalitas pencarian ke komponen tabel kita. Kita akan memungkinkan pengguna untuk mencari data dalam tabel dengan memasukkan istilah pencarian ke dalam input pencarian.

“`typescript
import React, { useState, useCallback } from ‘react’;

interface ColumnConfig {
Header: string;
accessor: keyof T;
Cell?: (row: T) => React.ReactNode;
sortable?: boolean;
}

interface Props {
data: T[];
columns: ColumnConfig[];
}

const SearchableTable = ({ data, columns }: Props) => {
const [searchTerm, setSearchTerm] = useState(”);

const handleSearch = (event: React.ChangeEvent) => {
setSearchTerm(event.target.value);
};

const filteredData = React.useMemo(() => {
const lowerSearchTerm = searchTerm.toLowerCase();
return data.filter(row => {
return columns.some(column => {
const value = String(row[column.accessor]).toLowerCase();
return value.includes(lowerSearchTerm);
});
});
}, [data, searchTerm, columns]);

return (

{/* Tabel seperti sebelumnya, menggunakan filteredData */}

);
};

export default SearchableTable;
“`

Beberapa perubahan penting telah dilakukan:

  • State searchTerm: Kita telah menambahkan state searchTerm untuk melacak istilah pencarian yang dimasukkan oleh pengguna.
  • handleSearch Fungsi: Kita telah menambahkan fungsi handleSearch yang dipanggil ketika pengguna mengubah nilai input pencarian. Fungsi ini memperbarui state searchTerm.
  • filteredData Variabel: Kita telah menambahkan variabel filteredData yang menyimpan data yang telah difilter. Variabel ini menggunakan useMemo untuk menghitung data yang difilter hanya ketika data atau searchTerm berubah.

Sekarang, ketika pengguna memasukkan istilah pencarian ke dalam input pencarian, tabel akan difilter untuk hanya menampilkan baris yang cocok dengan istilah pencarian.

7. Penomoran Halaman

Untuk set data yang besar, penomoran halaman sangat penting untuk meningkatkan kinerja dan pengalaman pengguna. Kita akan mengimplementasikan penomoran halaman untuk membagi data menjadi halaman yang lebih kecil.

“`typescript
import React, { useState, useMemo } from ‘react’;

interface ColumnConfig {
Header: string;
accessor: keyof T;
Cell?: (row: T) => React.ReactNode;
sortable?: boolean;
}

interface Props {
data: T[];
columns: ColumnConfig[];
pageSize?: number; // Jumlah baris per halaman
}

const PaginatedTable = ({ data, columns, pageSize = 10 }: Props) => {
const [currentPage, setCurrentPage] = useState(1);

const totalPages = useMemo(() => Math.ceil(data.length / pageSize), [data.length, pageSize]);
const paginatedData = useMemo(() => {
const startIndex = (currentPage – 1) * pageSize;
const endIndex = startIndex + pageSize;
return data.slice(startIndex, endIndex);
}, [data, currentPage, pageSize]);

const handlePageChange = (newPage: number) => {
setCurrentPage(newPage);
};

return (

{/* Tabel seperti sebelumnya, menggunakan paginatedData */}

Halaman {currentPage} dari {totalPages}

);
};

export default PaginatedTable;
“`

Beberapa perubahan penting telah dilakukan:

  • pageSize Properti: Kita telah menambahkan properti pageSize opsional ke interface Props. Properti ini menentukan jumlah baris yang akan ditampilkan per halaman. Jika properti pageSize tidak ditentukan, defaultnya adalah 10.
  • State currentPage: Kita telah menambahkan state currentPage untuk melacak halaman mana yang sedang ditampilkan.
  • totalPages Variabel: Kita telah menambahkan variabel totalPages yang menghitung jumlah total halaman.
  • paginatedData Variabel: Kita telah menambahkan variabel paginatedData yang menyimpan data yang akan ditampilkan pada halaman saat ini. Variabel ini menggunakan useMemo untuk menghitung data yang dipaginasi hanya ketika data, currentPage, atau pageSize berubah.
  • handlePageChange Fungsi: Kita telah menambahkan fungsi handlePageChange yang dipanggil ketika pengguna mengklik tombol “Sebelumnya” atau “Selanjutnya”. Fungsi ini memperbarui state currentPage.

Sekarang, tabel akan menampilkan hanya sejumlah baris per halaman yang ditentukan oleh properti pageSize. Pengguna dapat menavigasi antar halaman dengan mengklik tombol “Sebelumnya” dan “Selanjutnya”.

8. Styling

Styling adalah aspek penting dalam membuat tabel yang menarik dan mudah dibaca. Kita dapat menggunakan CSS atau pustaka styling seperti Styled Components atau Material UI untuk menata tabel kita.

Berikut adalah contoh styling dasar menggunakan CSS biasa:

“`css
table {
width: 100%;
border-collapse: collapse;
}

th,
td {
padding: 8px;
border: 1px solid #ddd;
text-align: left;
}

th {
background-color: #f2f2f2;
font-weight: bold;
}

tr:nth-child(even) {
background-color: #f2f2f2;
}
“`

Ini adalah contoh styling dasar yang dapat Anda sesuaikan sesuai dengan preferensi Anda.

9. Contoh Penggunaan

Sekarang mari kita lihat bagaimana kita dapat menggunakan komponen tabel generik kita dengan data yang berbeda.

“`typescript
import React from ‘react’;
import GenericTable from ‘./GenericTable’;

interface Product {
id: number;
name: string;
price: number;
description: string;
}

const products: Product[] = [
{
id: 1,
name: ‘Laptop’,
price: 1200,
description: ‘Laptop high-end dengan performa tinggi.’,
},
{
id: 2,
name: ‘Mouse’,
price: 25,
description: ‘Mouse ergonomis untuk kenyamanan maksimal.’,
},
{
id: 3,
name: ‘Keyboard’,
price: 75,
description: ‘Keyboard mekanis dengan switch yang responsif.’,
},
];

const productColumns = [
{
Header: ‘ID’,
accessor: ‘id’,
},
{
Header: ‘Nama’,
accessor: ‘name’,
},
{
Header: ‘Harga’,
accessor: ‘price’,
Cell: (product) => ${product.price.toFixed(2)},
},
{
Header: ‘Deskripsi’,
accessor: ‘description’,
},
];

const App = () => {
return (

);
};

export default App;
“`

Dalam contoh ini, kita membuat antarmuka Product dan array data products. Kita juga mendefinisikan array productColumns yang menentukan kolom yang akan ditampilkan dalam tabel. Kita kemudian menggunakan komponen GenericTable untuk menampilkan data produk.

10. Kesimpulan

Dalam artikel ini, kita telah menjelajahi bagaimana memanfaatkan tipado generik TypeScript untuk membuat komponen tabel React yang sangat fleksibel dan dapat digunakan kembali. Dengan menggunakan generik, kita dapat memastikan keamanan tipe, meningkatkan penggunaan ulang kode, dan mengurangi duplikasi kode.

Beberapa saran untuk pengembangan lebih lanjut:

  • Ekspor ke CSV: Tambahkan fungsionalitas untuk mengekspor data tabel ke file CSV.
  • Kustomisasi Sel: Berikan lebih banyak opsi untuk menyesuaikan tampilan sel, seperti menambahkan ikon atau tautan.
  • Integrasi API: Buat komponen tabel dapat mengambil data dari API secara dinamis.
  • Pengujian Unit: Tulis pengujian unit untuk memastikan bahwa komponen tabel berfungsi dengan benar.

Dengan mengikuti langkah-langkah ini, Anda dapat membuat komponen tabel React yang kuat dan fleksibel yang dapat digunakan untuk menampilkan berbagai jenis data dalam aplikasi Anda.

“`

omcoding

Leave a Reply

Your email address will not be published. Required fields are marked *