React ile Özel Sayfalama (Pagination) Bileşeni Oluşturma
Büyük veri kümelerini tek sayfada göstermek hem performans hem kullanıcı deneyimi açısından zordur.
Bu rehberde, React kullanarak dinamik ve yeniden kullanılabilir bir sayfalama bileşeni oluşturacağız.
Uygulamamızda dünya ülkelerini parçalara ayırarak gösterecek, gezinme mantığını sıfırdan inşa edeceğiz.
🧠 Aşama 1 – Konunun Özeti
Ana teknik konu: React ile özel, özelleştirilebilir sayfalama bileşeni geliştirmek.
Çözdüğü problem: Büyük veri setlerini (örneğin binlerce kayıt) kullanıcıya parça parça göstermek.
Adımlar:
- Proje kurulumu ve kütüphanelerin yüklenmesi
- Ülke kartı bileşeni oluşturma
- Sayfalama mantığı (sayfa numaraları, limit, komşular)
- Ana uygulamada birleştirme
- SCSS ile özel stiller
🚀 Aşama 2 – Projeyi Hazırlama
Yeni bir React uygulaması başlatın:
npx create-react-app rabisu-sayfalama
cd rabisu-sayfalama
Gerekli bağımlılıkları yükleyin:
Bootstrap stillerini projeye dahil edin:
// src/index.js
import "bootstrap/dist/css/bootstrap.min.css";
Bayrak görsellerini ekleyin:
mkdir public/img
cp -R node_modules/react-flags/vendor/flags public/img
Bu adımlar, hem veri hem de stil altyapısını hazırlamış olur.
🧩 Aşama 3 – Ülke Kartı Bileşenini Oluşturma
Her ülkeyi gösteren CountryCard bileşenini yazıyoruz:
import React from 'react';
import PropTypes from 'prop-types';
import Flag from 'react-flags';
const UlkeKarti = ({ ulke }) => {
const { cca2: kod = '', region, name = {} } = ulke;
return (
<div className="col-sm-6 col-md-4 ulke-kart">
<div className="border rounded bg-light d-flex align-items-center p-0 my-3">
<div className="border-right px-2 bg-white">
<Flag country={kod} format="png" pngSize={64} basePath="./img/flags" />
</div>
<div className="px-3">
<span className="font-weight-bold text-dark d-block">{name.common}</span>
<span className="text-secondary text-uppercase">{region}</span>
</div>
</div>
</div>
);
};
UlkeKarti.propTypes = {
ulke: PropTypes.shape({
cca2: PropTypes.string.isRequired,
region: PropTypes.string.isRequired,
name: PropTypes.shape({
common: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
};
export default UlkeKarti;
Bu bileşen, ülkenin adını, bölgesini ve bayrağını gösterir.
🧮 Aşama 4 – Sayfalama (Pagination) Bileşenini Yazma
Sayfa numaralarını dinamik oluşturacak Pagination bileşeni:
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
const LEFT_PAGE = 'LEFT';
const RIGHT_PAGE = 'RIGHT';
const range = (from, to, step = 1) => {
const out = [];
for (let i = from; i <= to; i += step) out.push(i);
return out;
};
class Sayfalama extends Component {
constructor(props) {
super(props);
const { toplamKayit = 0, sayfaLimiti = 18, sayfaKomsulari = 1 } = props;
this.sayfaLimiti = sayfaLimiti;
this.toplamKayit = toplamKayit;
this.sayfaKomsulari = Math.max(0, Math.min(sayfaKomsulari, 2));
this.toplamSayfa = Math.ceil(this.toplamKayit / this.sayfaLimiti);
this.state = { aktifSayfa: 1 };
}
componentDidMount() {
this.sayfayaGit(1);
}
sayfayaGit = (sayfa) => {
const { onSayfaDegisti = f => f } = this.props;
const aktifSayfa = Math.max(1, Math.min(sayfa, this.toplamSayfa));
const veri = {
aktifSayfa,
toplamSayfa: this.toplamSayfa,
sayfaLimiti: this.sayfaLimiti,
toplamKayit: this.toplamKayit
};
this.setState({ aktifSayfa }, () => onSayfaDegisti(veri));
};
handleClick = sayfa => e => { e.preventDefault(); this.sayfayaGit(sayfa); };
handleSolaKaydir = e => { e.preventDefault(); this.sayfayaGit(this.state.aktifSayfa - 1); };
handleSagaKaydir = e => { e.preventDefault(); this.sayfayaGit(this.state.aktifSayfa + 1); };
sayfaNumaralariniGetir = () => {
const { toplamSayfa } = this;
const { aktifSayfa } = this.state;
const toplamNumara = (this.sayfaKomsulari * 2) + 3;
if (toplamSayfa <= toplamNumara) return range(1, toplamSayfa);
const start = Math.max(2, aktifSayfa - this.sayfaKomsulari);
const end = Math.min(toplamSayfa - 1, aktifSayfa + this.sayfaKomsulari);
let sayfalar = range(start, end);
if (start > 2) sayfalar = [LEFT_PAGE, ...sayfalar];
if (end < toplamSayfa - 1) sayfalar = [...sayfalar, RIGHT_PAGE];
return [1, ...sayfalar, toplamSayfa];
};
render() {
if (!this.toplamKayit || this.toplamSayfa === 1) return null;
const { aktifSayfa } = this.state;
const sayfalar = this.sayfaNumaralariniGetir();
return (
<Fragment>
<nav aria-label="Sayfalama">
<ul className="pagination">
{sayfalar.map((sayfa, i) => {
if (sayfa === LEFT_PAGE)
return <li key={i}><a href="#" onClick={this.handleSolaKaydir}>«</a></li>;
if (sayfa === RIGHT_PAGE)
return <li key={i}><a href="#" onClick={this.handleSagaKaydir}>»</a></li>;
return (
<li key={i} className={`page-item${aktifSayfa === sayfa ? ' active' : ''}`}>
<a href="#" className="page-link" onClick={this.handleClick(sayfa)}>{sayfa}</a>
</li>
);
})}
</ul>
</nav>
</Fragment>
);
}
}
Sayfalama.propTypes = {
toplamKayit: PropTypes.number.isRequired,
sayfaLimiti: PropTypes.number,
sayfaKomsulari: PropTypes.number,
onSayfaDegisti: PropTypes.func
};
export default Sayfalama;
Bu sınıf, toplam kayıt sayısına göre sayfa numaralarını hesaplar ve tıklamalarda sayfa geçişini yönetir.
⚙️ Aşama 5 – Ana Uygulama (App.js)
import React, { Component } from 'react';
import Countries from 'countries-api';
import './App.scss';
import Sayfalama from './components/Sayfalama';
import UlkeKarti from './components/UlkeKarti';
class App extends Component {
state = { tumUlkeler: [], guncelUlkeler: [], aktifSayfa: null, toplamSayfa: null };
componentDidMount() {
const { data: tumUlkeler = [] } = Countries.findAll();
this.setState({ tumUlkeler });
}
onSayfaDegisti = veri => {
const { tumUlkeler } = this.state;
const { aktifSayfa, toplamSayfa, sayfaLimiti } = veri;
const baslangic = (aktifSayfa - 1) * sayfaLimiti;
const guncelUlkeler = tumUlkeler.slice(baslangic, baslangic + sayfaLimiti);
this.setState({ aktifSayfa, guncelUlkeler, toplamSayfa });
};
render() {
const { tumUlkeler, guncelUlkeler, aktifSayfa, toplamSayfa } = this.state;
const toplamUlke = tumUlkeler.length;
if (toplamUlke === 0) return null;
return (
<div className="container mb-5">
<div className="row py-5">
<div className="d-flex justify-content-between align-items-center w-100 px-4 py-3">
<h2><strong>{toplamUlke}</strong> Ülke</h2>
{aktifSayfa && <span>Sayfa {aktifSayfa}/{toplamSayfa}</span>}
</div>
<Sayfalama toplamKayit={toplamUlke} sayfaLimiti={18} sayfaKomsulari={1} onSayfaDegisti={this.onSayfaDegisti} />
{guncelUlkeler.map(u => <UlkeKarti key={u.cca3} ulke={u} />)}
</div>
</div>
);
}
}
export default App;
🎨 SCSS – Basit ve Modern Stil
ul.pagination {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
a.page-link:hover { background-color: #f7f7f7; }
.active .page-link {
background-color: #ddd;
color: #000;
}
}
🙋♀️ Sıkça Sorulan Sorular
- Sayfalama yerine sonsuz kaydırma (infinite scroll) kullanabilir miyim?
Evet. Ancak sayfalama, kullanıcıya veri kümesi üzerinde daha fazla kontrol sunar.
- toplamKayit neden zorunlu?
Toplam sayfa sayısını hesaplamak için gereklidir.
- Gerçek API verilerinde performans sorunları olur mu?
Büyük veri kümelerinde slice yerine LIMIT ve OFFSET kullanarak API’den dinamik veri çekmek gerekir.
- Hook'lar ile yazabilir miyim?
Evet, aynı mantığı useState ve useEffect ile daha sade biçimde uygulayabilirsiniz.
☁️ Sonuç
Artık React ile tamamen özelleştirilebilir bir sayfalama sistemi oluşturmayı öğrendiniz. Bu yapı, kullanıcı dostu, performanslı ve modülerdir.
💡 Projenizi Rabisu Bulut üzerinde barındırarak yüksek performansla yayınlayabilirsiniz.