---
title: 'Bileşik Bileşenler'
source: 'https://academia.sh/tr/kurslar/bilesen-tabanli-gelistirme/bilesik-bilesenler'
course: 'Bileşen Tabanlı Arayüz Geliştirme'
language: tr
updated: '2026-08-17T18:09:13+00:00'
license: 'CC BY-SA 4.0'
---

# Bileşik Bileşenler

Tek bir durumu paylaşan bileşen ailesi; örtük paylaşımın özellik yüzeyine etkisi, ilişkisel özniteliklerin bileşen tarafından kurulması ve örtük sözleşmenin çalışma zamanında denetlenmesi.

Süzgeç paneli büyüdükçe iki bölüme ayrıldı: eşik ayarları ve tarih aralığı. İkisi aynı anda
görünmüyor; kullanıcı bir sekme başlığına basarak birinden diğerine geçiyor. Yapı üç
parçadan oluşuyor — başlıkların listesi, başlıkların kendisi ve gövdeler — ve üçü de
"hangisi etkin" bilgisini bilmek zorunda.

Bu bilgiyi her parçaya özellik olarak vermek, ilk derste ölçülen özellik zincirini geri
getirir. Her parçanın kendi kopyasını tutması ise iki gövdenin aynı anda görünmesine izin
verir. Gereken şey, ayrı bileşenler olarak yazılmış ama tek bir durumu paylaşan bir ailedir.

## Aile ve Örtük Paylaşım

**Bileşik bileşen (compound component)**, tek başına anlamlı olmayan üyelerden kurulu bir
bileşen kümesidir. Üst üye durumu tutar ve sağlar; alt üyeler o durumu doğrudan okur.
Çağıran, durumu üyelerin arasında elden ele taşımaz.

Paylaşım, Bağlam Aktarımı dersindeki mekanizmayla yapılır: üst üye bir değeri ağacın o
noktasında sağlar, alt üyeler kendi çizimleri sırasında okur. Bunun çalışması için tek bir
koşul vardır — alt üyeler üst üyenin çiziminden **sonra** çizilmelidir. Bileşen ağacının
tembel kurulması bu koşulu kendiliğinden sağlar.

Üyelerin üst bileşenin bir özelliği olarak adlandırılması (`Sekmeler.Başlık` gibi) teknik
bir zorunluluk değildir; aidiyeti kaynak metinde görünür kılan bir adlandırma kararıdır.

```js
// bilesik.mjs — birlikte calisan bilesen ailesi ve ortulu sozlesmesi
const oge = (ad, ozn = {}, ...cocuklar) => ({ ad, ozn, cocuklar: cocuklar.flat().filter(Boolean) });
const metin = (deger) => ({ ad: "#metin", ozn: {}, cocuklar: [], metin: deger });

// Baglam yigiti: cocuklar ust bilesenin cizimi sirasinda calistigi icin okuyabilirler.
const yigit = [];
function baglamSagla(deger, cocuklariCiz) {
  yigit.push(deger);
  try { return cocuklariCiz(); } finally { yigit.pop(); }
}
function baglamOku(cagiran) {
  const d = yigit[yigit.length - 1];
  if (!d) throw new Error(`${cagiran} yalnızca Sekmeler bileşeninin içinde kullanılabilir`);
  return d;
}

// Ozellik sayaci: cagiran tarafin yazdigi ozellikleri say.
let yazilanOzellik = 0;
const say = (o) => { yazilanOzellik += Object.keys(o).length; return o; };

// --- Bilesik bilesen ailesi ---
const Sekmeler = (o) => {
  say(o);
  const paylasilan = { etkin: o.etkin, sec: o.sec, kok: o.kimlik };
  return oge("div", { class: "sekmeler" }, baglamSagla(paylasilan, o.cocuklar));
};

Sekmeler.Liste = (o) => {
  say(o);
  baglamOku("Sekmeler.Liste");
  return oge("div", { role: "tablist" }, o.cocuklar());
};

Sekmeler.Baslik = (o) => {
  say(o);
  const b = baglamOku("Sekmeler.Başlık");
  const secili = b.etkin === o.ad;
  return oge("button", {
    id: `${b.kok}-baslik-${o.ad}`,
    role: "tab",
    type: "button",
    "aria-selected": String(secili),
    "aria-controls": `${b.kok}-govde-${o.ad}`,
    tabindex: secili ? 0 : -1,
  }, metin(o.metin));
};

Sekmeler.Govde = (o) => {
  say(o);
  const b = baglamOku("Sekmeler.Gövde");
  if (b.etkin !== o.ad) return null;
  return oge("div", {
    id: `${b.kok}-govde-${o.ad}`,
    role: "tabpanel",
    "aria-labelledby": `${b.kok}-baslik-${o.ad}`,
    tabindex: 0,
  }, o.cocuklar());
};

// --- Kullanim ---
const yaz = (d, g = 0) => {
  if (!d) return [];
  if (d.metin !== undefined) return [`${"  ".repeat(g)}"${d.metin}"`];
  const oznMetni = Object.entries(d.ozn).map(([a, v]) => ` ${a}="${v}"`).join("");
  return [`${"  ".repeat(g)}<${d.ad}${oznMetni}>`, ...d.cocuklar.flatMap((c) => yaz(c, g + 1))];
};

yazilanOzellik = 0;
const agac = Sekmeler({
  kimlik: "suzgec", etkin: "esik", sec: () => {},
  cocuklar: () => [
    Sekmeler.Liste({ cocuklar: () => [
      Sekmeler.Baslik({ ad: "esik", metin: "Eşik" }),
      Sekmeler.Baslik({ ad: "aralik", metin: "Tarih aralığı" }),
    ] }),
    Sekmeler.Govde({ ad: "esik", cocuklar: () => [metin("-40 ile 40 arası")] }),
    Sekmeler.Govde({ ad: "aralik", cocuklar: () => [metin("son 24 saat")] }),
  ],
});
const bilesikOzellik = yazilanOzellik;
console.log(yaz(agac).join("\n"));

// Ayni agac, paylasilan durum her cocuga elle verilerek.
yazilanOzellik = 0;
const AcikBaslik = (o) => { say(o); return oge("button", {
  id: `${o.kok}-baslik-${o.ad}`, role: "tab", type: "button",
  "aria-selected": String(o.etkin === o.ad), "aria-controls": `${o.kok}-govde-${o.ad}`,
  tabindex: o.etkin === o.ad ? 0 : -1 }, metin(o.metin)); };
const AcikGovde = (o) => { say(o); return o.etkin !== o.ad ? null : oge("div", {
  id: `${o.kok}-govde-${o.ad}`, role: "tabpanel",
  "aria-labelledby": `${o.kok}-baslik-${o.ad}`, tabindex: 0 }, o.cocuklar()); };
const acikAgac = oge("div", { class: "sekmeler" },
  oge("div", { role: "tablist" },
    AcikBaslik({ kok: "suzgec", etkin: "esik", sec: () => {}, ad: "esik", metin: "Eşik" }),
    AcikBaslik({ kok: "suzgec", etkin: "esik", sec: () => {}, ad: "aralik", metin: "Tarih aralığı" })),
  AcikGovde({ kok: "suzgec", etkin: "esik", ad: "esik", cocuklar: () => [metin("-40 ile 40 arası")] }),
  AcikGovde({ kok: "suzgec", etkin: "esik", ad: "aralik", cocuklar: () => [metin("son 24 saat")] }));

console.log("\niki ağaç aynı mı:", JSON.stringify(agac) === JSON.stringify(acikAgac));
console.log(`çağıranın yazdığı özellik — bileşik: ${bilesikOzellik}, açık aktarım: ${yazilanOzellik}`);

// --- Ortulu sozlesmenin denetimi ---
console.log("\naile dışında kullanım:");
try { Sekmeler.Baslik({ ad: "esik", metin: "Eşik" }); }
catch (h) { console.log(" ", h.message); }

console.log("\nyanlış ada bağlanan gövde:");
yigit.length = 0;
const kirik = Sekmeler({
  kimlik: "suzgec", etkin: "esik", sec: () => {},
  cocuklar: () => [
    Sekmeler.Liste({ cocuklar: () => [Sekmeler.Baslik({ ad: "esik", metin: "Eşik" })] }),
    Sekmeler.Govde({ ad: "esikk", cocuklar: () => [metin("-40 ile 40 arası")] }),
  ],
});
const topla = (d, f, c = []) => { if (!d) return c; if (f(d)) c.push(d); d.cocuklar?.forEach((x) => topla(x, f, c)); return c; };
const basliklar = topla(kirik, (d) => d.ozn.role === "tab");
const govdeler = topla(kirik, (d) => d.ozn.role === "tabpanel");
console.log("  başlık sayısı:", basliklar.length, "| gövde sayısı:", govdeler.length);
for (const b of basliklar) {
  const hedef = govdeler.find((g) => g.ozn.id === b.ozn["aria-controls"]);
  console.log(`  ${b.ozn.id} → aria-controls="${b.ozn["aria-controls"]}" ${hedef ? "hedef var" : "HEDEF YOK"}`);
}
```

```
<div class="sekmeler">
  <div role="tablist">
    <button id="suzgec-baslik-esik" role="tab" type="button" aria-selected="true" aria-controls="suzgec-govde-esik" tabindex="0">
      "Eşik"
    <button id="suzgec-baslik-aralik" role="tab" type="button" aria-selected="false" aria-controls="suzgec-govde-aralik" tabindex="-1">
      "Tarih aralığı"
  <div id="suzgec-govde-esik" role="tabpanel" aria-labelledby="suzgec-baslik-esik" tabindex="0">
    "-40 ile 40 arası"

iki ağaç aynı mı: true
çağıranın yazdığı özellik — bileşik: 13, açık aktarım: 18

aile dışında kullanım:
  Sekmeler.Başlık yalnızca Sekmeler bileşeninin içinde kullanılabilir

yanlış ada bağlanan gövde:
  başlık sayısı: 1 | gövde sayısı: 0
  suzgec-baslik-esik → aria-controls="suzgec-govde-esik" HEDEF YOK
```

## Ailenin Ürettiği İlişkiler

Çıktının ilk bölümünde çağıranın hiç yazmadığı beş öznitelik var: iki kimlik, `aria-selected`,
`aria-controls` ve `aria-labelledby`. Bunlar üyeler arasındaki **ilişkiyi** kuran
özniteliklerdir; başlık hangi gövdeyi denetlediğini, gövde hangi başlıkla adlandırıldığını
bunlarla bildirir.

İlişkiyi ailenin kurması iki nedenle önemlidir. Birincisi, kimliklerin benzersizliği tek bir
kökten türetilir; aynı sayfada ikinci bir sekme grubu farklı bir kök adı alır ve kimlikler
çakışmaz. İkincisi, ilişki çağırana bırakılmaz. Web'in Temelleri ve HTML kursundaki
Etiket–Alan İlişkisi dersinde görülen sorun burada da geçerlidir: elle yazılan bir ilişki
bir yerde unutulur, unutulduğunda görsel olarak hiçbir şey değişmez ve yalnızca yardımcı
teknoloji kullanıcısı etkilenir.

Odak yönetimi de aileye aittir. Çıktıda etkin olmayan başlığın `tabindex` değeri -1;
Tarayıcı ve Web Platformu kursundaki dönen sekme durağı düzeni budur. Sekme listesi klavye
sırasında tek durak tutar, listenin içinde ok tuşlarıyla gezilir.

## Örtük Paylaşımın Bedeli

İki ağaç birebir aynı. Fark yalnızca çağıranın yazdığı özellik sayısında: bileşik ailede 13,
açık aktarımda 18. Aradaki beş, paylaşılan durumun her üyeye elle verilmesinden gelir ve üye
sayısıyla birlikte artar. Dört sekmeli bir grupta fark on olur.

Kazancın karşılığı **keşfedilebilirliğin** azalmasıdır. `Sekmeler.Başlık` bileşeninin
imzasına bakıldığında hangi durumu okuduğu görünmez; imzada yalnızca `ad` ve `metin` vardır.
Bileşenin gerçek girdisi imzasında yazmayan bir bağlamdır. Bu, ilk derste sarmalayan
bileşenin gövdesinde biriken sessiz kararların bir benzeridir; burada karar gövdede değil,
ağaçtaki konumdadır.

İkinci bedel, üyenin tek başına taşınamamasıdır. `Sekmeler.Başlık` ailesinden koparıldığında
çalışmaz. Bu bir kusur değil tanımdır — ancak tanımın çalışma zamanında görünür olması
gerekir.

## Örtük Sözleşmenin Denetimi

Çıktının üçüncü bölümü, aile dışında çağrılan üyenin ne yaptığını gösteriyor: adıyla birlikte
nereye ait olduğunu söyleyen bir istisna. Bağlamı okumadan önce varlığını sınamak tek
satırlık bir denetimdir ve bileşik bileşenlerde zorunludur. Denetim olmasaydı, tanımsız bir
değerden alan okumaya çalışan anlamsız bir hata alınırdı.

Dördüncü bölüm daha sinsi bir kusuru gösteriyor. Gövdenin adı `esikk` yazılmış; başlık
`esik` adına bağlı. Hiçbir istisna atılmıyor, çünkü her iki üye de aile içinde ve bağlamı
okuyabiliyor. Sonuç ağaçta bir sekme başlığı ve hiç gövde olması, başlığın `aria-controls`
özniteliğinin var olmayan bir kimliği göstermesi. Bu, Şablonlar ve Yuvalar dersindeki
eşleşmeyen yuva adıyla aynı sınıftan bir hatadır: yazım hatası sessizce kaybolur.

Bu tür kusurların denetimi iki yerde yapılabilir. Çalışma zamanında, üst üye kendi altındaki
üyelerin adlarını toplayıp eşleşmeyenleri bildirir; bedeli, ağaç üzerinde ek bir geçiştir.
Tip düzeyinde, üye adları sabit bir birlik tipinden alınır ve eşleşmeyen ad derlenmez;
bedeli, adların çalışma zamanında üretilememesidir. İkinci yol daha ucuzdur ve
yakalayabildiği hatayı erken yakalar.

## Ne Zaman Aile Kurulmaz

Bileşik bileşen, üyeler arasında **gerçek bir paylaşılan durum** olduğunda doğrudur. Sekme
grubunda böyle bir durum vardır: hangisinin etkin olduğu. Ölçüm tablosunun başlık ve gövde
bölümlerinde yoktur; ikisi de yalnızca yan yana durur. Böyle bir yerde aile kurmak, örtük
paylaşımın bedelini karşılığı olmadan ödemektir; yuva tabanlı bileşim yeter.

İkinci sınır üye sayısıdır. Yedi üyeli bir ailede çağıranın öğrenmesi gereken şey bir
bileşen değil, küçük bir dildir; üyeler arasındaki geçerli iç içe geçme kuralları da bu
dilin sözdizimi olur. Böyle bir yüzey ancak yeniden kullanım sayısı yüksekse karşılığını
verir.

Üçüncü sınır, ailenin sıraya bağımlı olmasıdır. Üst üye alt üyelerin belirli bir sırada
geldiğini varsayıyorsa, çağıranın araya koyacağı bir sarmalayıcı düzeni bozar. Bağlam
üzerinden kurulan paylaşım bu bağımlılığı taşımaz — bağlam ağacın derinliği boyunca okunur,
sıra gözetmez — ve bu, doğrudan çocuk listesini tarayan tasarımlara üstünlüğüdür.

## Özet

- Bileşik bileşen, tek başına anlamlı olmayan üyelerden kurulu ve tek bir durumu bağlam
  üzerinden paylaşan bir ailedir.
- Paylaşımın koşulu, alt üyelerin üst üyenin çiziminden sonra çizilmesidir; bileşen ağacının
  tembel kurulması bunu sağlar.
- Aile, üyeler arasındaki kimlik ve ilişki özniteliklerini kendisi üretir; bu, elle yazılan
  ilişkilerde görülen sessiz kusurları ortadan kaldırır.
- Örtük paylaşım çağıranın yazdığı özellik sayısını düşürür, karşılığında üyenin girdisini
  imzasından okunamaz hâle getirir.
- Aile dışında kullanım çalışma zamanında istisna ile bildirilmelidir; üyeler arası ad
  eşleşmesi ise tip düzeyinde ya da ağaç taramasıyla denetlenir.
- Paylaşılan durum yoksa aile kurulmaz; yuva tabanlı bileşim aynı yapıyı bedelsiz verir.

## Sonraki Adım

Sekme ailesi bir şeyi hâlâ birbirine karıştırıyor: hangi sekmenin etkin olduğunu tutmakla
sekmenin nasıl göründüğüne karar vermek aynı bileşende. Bu, küçük bir ailede sorun
çıkarmaz, ama ölçüm tablosunda çıkarır — tablo veriyi getiriyor, sıralıyor, sayfalıyor ve
aynı zamanda hücreleri çiziyor. Böyle bir bileşen ne veri katmanı değişmeden sınanabilir ne
de başka bir veri kaynağıyla yeniden kullanılabilir. Sonraki ders bu iki sorumluluğu ayırır
ve ayrımın sınanabilirlik üzerindeki etkisini sahte bağımlılık sayısıyla ölçer.
