React Table ile Tablo Oluşturma
Efenim uzatmadan direkt dalıyorum.
Not: Ben anlarım diyenler için projenin son hali buradadır.
Not 2: Test etmek isteyenleri buraya alalım.
İlk yapmanız gereken şey bir react projesi oluşturmak.
npx create-react-app datatable
Ardından hızlıca şu kurulumları aradan çıkartalım.
React table kullanmak için:
npm install react-table --save
Biraz şekil vermek için:
npm install bootstrap font-awesome@4.7 jquery popper.js --save
Evet. Hepsi bittiyse App.js dosyamızın için şu hale getirelim.
function App() {
return <div className="container-fluid"></div>
}
export default App
Görüldüğü gibi tertemiz. Sadece bootstrap class ı ekledik o kadar.
Bir de anadizinde bulunan index.js dosyanıza şunları ekleyin.
import 'jquery'
import 'popper.js/dist/umd/popper'
import 'bootstrap/dist/js/bootstrap'
import 'bootstrap/dist/css/bootstrap.css'
import 'font-awesome/css/font-awesome.css'
Sonrasında src altında components adında bir dizin açalım ve içine "Datatables.css" ile "Datatable.jsx" dosyalarını oluşturalım.
Tamaaam. Temel kurulumlar bu kadar.
Alın size bir de dummy data. Buraya tıklayıp indirebilirsiniz.
İndirdiğiniz json dosyasını da data.json olarak kaydedip anadizine atın.
Başlıyoruz.
App.js dosyasını açalım. İndirdiğimiz ve kaydettiğimiz data.json dosyasını dahil edelim.
import datalar from './data.json'
Ardından oluşturacağımız data değişkenine useMemo kullanarak bu değerleri atayalım.
const data = React.useMemo(() => {
return datalar
}, [])
Daha sonra sütun isimlerini tekrar useMemo ile belirtelim.
const columns = React.useMemo(
() => [
{
Header: 'Ad Soyad',
Footer: 'Ad Soyad',
accessor: 'name',
},
{
Header: 'Profil İsmi',
Footer: 'Profil İsmi',
accessor: 'profil_name',
},
{
Header: 'Tarih',
Footer: 'Tarih',
accessor: 'date',
},
{
Header: 'score',
Footer: 'score',
accessor: 'score',
},
{
Header: 'Adres',
Footer: 'Adres',
accessor: 'location',
},
{
Header: 'Düzenle',
accessor: (originalRow, rowIndex) => (
<div className="d-flex align-items-center">
<button
className="btn btn-warning mr-2 btn-sm"
onClick={() => handleEdit(originalRow)}
>
Edit
</button>
<button
className="btn btn-danger btn-sm"
onClick={() => handleDelete(originalRow)}
>
Delete
</button>
</div>
),
id: 'action',
Footer: 'Düzenle',
},
],
[],
)
Anlaşılacağı gibi tablo sütunlarının başlıkları "Ad Soyad, Profil İsmi, Tarih, Score, Adres ve Düzenle" olacaktır.
Buradaki accesor key'i, data.json dosyası içindeki key değerlerini gösteriyor
Düzenle kısmı için 2 adet metodumuz var. Birisi "Edit" diğeri "Delete".
Onları da oluşturuverelim.
const handleEdit = (row) => {
console.log(row)
}
const handleDelete = (row) => {
console.log(row)
}
Artık bu kısım size kalmış olacak haliyle. API ile iletişim kuracaksınız.
Daha sonra return kısmında da components içinde oluşturduğumuz Datatable ı çağııracağız. İçine de birkaç adet prop sıkıştıralım.
<Datatable data={data} columns={columns} tableBaslik="Personeller" />
App.js dosyamızın son hali şu şekilde olacaktır.
import './App.css'
import React from 'react'
import Datatable from './components/Datatable'
import datalar from './data.json'
function App() {
const data = React.useMemo(() => {
return datalar
}, [])
const columns = React.useMemo(
() => [
{
Header: 'Ad Soyad',
Footer: 'Ad Soyad',
accessor: 'name', // accessor is the "key" in the data
},
{
Header: 'Profil İsmi',
Footer: 'Profil İsmi',
accessor: 'profil_name',
},
{
Header: 'Tarih',
Footer: 'Tarih',
accessor: 'date',
},
{
Header: 'score',
Footer: 'score',
accessor: 'score',
},
{
Header: 'Adres',
Footer: 'Adres',
accessor: 'location',
},
{
Header: 'Düzenle',
accessor: (originalRow, rowIndex) => (
<div className="d-flex align-items-center">
<button
className="btn btn-warning mr-2 btn-sm"
onClick={() => handleEdit(originalRow)}
>
Edit
</button>
<button
className="btn btn-danger btn-sm"
onClick={() => handleDelete(originalRow)}
>
Delete
</button>
</div>
),
id: 'action',
Footer: 'Düzenle',
},
],
[],
)
const handleEdit = (row) => {
console.log(row)
}
const handleDelete = (row) => {
console.log(row)
}
return (
<div className="container-fluid">
<Datatable data={data} columns={columns} tableBaslik="Personeller" />
</div>
)
}
export default App
Şimdi gelelim Datatable.jsx içine..
Öncelikle yapacaklarımızı yazalım.
- Sıralama
- Sayfalama
- Arama ( Filtreleme )
Dolayısı ile react-table dan bunları import etmemiz gerekiyor. Tabi tablo oluşturmak için olmazsa olmaz useTable hook'umuzu da unutmuyoruz.
import {
useTable,
useSortBy,
useGlobalFilter,
useFilters,
usePagination,
} from 'react-table'
Hemen yukarıda props olarak birkaç şey göndermiştik. Onları da çıkartıverelim.
const { data, columns, tableBaslik } = props
useTable içinden de istediğimiz property leri çıkartalım. Anlayacaksınız zaten.
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
state,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
preGlobalFilteredRows,
setGlobalFilter,
page,
prepareRow,
} = useTable(
{
columns,
data,
initialState: {
hiddenColumns: ['score'],
},
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination,
)
Notlar:
- getTableProps: Zorunlu alandır.
- getTableBodyProps: Zorunlu alandır.
- headerGroups: Tablo header bilgisi için gerekli. <thead>
- footerGroups: Tablo footer bilgisi için gerekli. <tfoot>
- state: tablonun son halinin bilgilerini bulundurur.
- nextPage: Bir fonksiyondur. state.pageIndex değerini 1 arttırır.
- previousPage: Bir fonksiyondur. state.pageIndex değerini 1 azaltır.
- canNextPage: İleri giderken eğer daha sayfa var ise true döndürür. ( Bunu son sayfada ileri butonunu disabled yapmak için kullanacağız. )
- canPreviousPage: Geri giderken eğer 0 dan daha büyük bir sayfa var ise true döndürür. ( Bunu sayfada geri doğru giderken eğer ilk sayfada ise geri butonunu disabled yapmak için kullanacağız. )
- pageOptions: 0 dan başlayarak sayfa sayısını tutan bir array döndürür.
- page: Mevcut sayfadaki satırların olduğu bir array dir.
- preGlobalFilteredRows: Filtreleme işlemi yapmadan önceki tüm satırların bulunduğu bir arraydir.
- setGlobalFilter: state.globalFilter değerini güncellemek için kullanılır. ( Biz bu değeri ileride "GlobalFilter.jsx" adında bir dosya oluşturacağız ve orada kullanacağız.)
- prepareRow: Zorunlu alandır.
state içinden kullanağımız "pageIndex" değerini çıkartalım.
const { pageIndex } = state
Datatable.jsx dosyamızın son hali aşağıdaki gibi olacak.
import React from 'react'
import './Datatable.css'
import {
useTable,
useSortBy,
useGlobalFilter,
useFilters,
usePagination,
} from 'react-table'
import GlobalFilter from './GlobalFilter'
const Datatable = (props) => {
const { data, columns, tableBaslik } = props
const {
getTableProps,
getTableBodyProps,
headerGroups,
footerGroups,
nextPage,
previousPage,
canNextPage,
canPreviousPage,
pageOptions,
state,
preGlobalFilteredRows,
setGlobalFilter,
page,
prepareRow,
} = useTable(
{
columns,
data,
initialState: {
hiddenColumns: ['score'],
},
},
useFilters,
useGlobalFilter,
useSortBy,
usePagination,
)
const { pageIndex } = state
console.log('headerGroups', headerGroups)
// console.log('footerGroups', footerGroups)
return (
<>
<div className="row">
<div className="col-md-12 p-3 ">
<div className="card">
<div className="card-header">
<h5>
<i className="fa fa-user" aria-hidden="true"></i> {tableBaslik}
</h5>
</div>
<div className="card-body">
<div className="globalFilterMainDiv">
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
</div>
<div className="table-responsive">
<table
{...getTableProps()} // Tablo proplarını buradan alıyoruz.
className="table table-striped table-hover"
>
<thead>
{headerGroups.map((
headerGroup, // Tablo başlıklarını buradan alıyoruz.
) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
{...column.getHeaderProps(
column.getSortByToggleProps, // sort edebilmek için
)}
>
{column.render('Header')}
<span>
{column.isSortedDesc ? ( // aşağı ve yukarı yönlü okları gösteriyoruz.
<i
className=" fa fa-sort"
aria-hidden="true"
></i>
) : (
<i
className=" fa fa-sort"
aria-hidden="true"
></i>
)}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
<tfoot>
{footerGroups.map((group) => (
<tr {...group.getFooterGroupProps()}>
{group.headers.map((column) => (
<th {...column.getFooterProps()}>
{column.render('Footer')}
</th>
))}
</tr>
))}
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className="dataTable-pagination d-flex justify-content-end">
<span className="pageOptionsSpan">
{' '}
{pageIndex + 1} / {pageOptions.length}{' '}
</span>
<button
className="btn btn-primary btn-sm mr-2"
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
<i className="fa fa-chevron-left" aria-hidden="true"></i>
</button>
<button
className="btn btn-primary btn-sm"
onClick={() => nextPage()}
disabled={!canNextPage}
>
<i className="fa fa-chevron-right" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
</>
)
}
export default Datatable
Son olarak GlobalFilter.jsx dosyamızı da şuraya ekleyelim.
import React from 'react'
import { useAsyncDebounce } from 'react-table'
const GlobalFilter = ({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) => {
const count = preGlobalFilteredRows.length
const [value, setValue] = React.useState(globalFilter)
const onChange = useAsyncDebounce((value) => {
setGlobalFilter(value || undefined)
}, 200)
return (
<div className="mb-3 row">
<label htmlFor="globalFilterInput" className="col-sm-1 col-form-label">
Ara:
</label>
<div className="col-sm-11">
<input
className="form-control"
id="globalFilterInput"
value={value || ''}
onChange={(e) => {
setValue(e.target.value)
setGlobalFilter(e.target.value || undefined)
}}
placeholder={`${count} kayıt içinde arama yap...`}
/>
</div>
</div>
)
}
export default GlobalFilter
Tahmin edeceğiniz üzere arama inputumuzun çalışmasını burada gerçekleştiriyoruz.
Son halinin görseli aşağıdaki gibi olacaktır.
Yazarken çok sıkıldım. Atladığım yerler zorunlu olarak kütüphaneden gelen değerlerdir.
Neyse hepsi bu kadar..
Bir kenarda dursun..
Yorum Yap