---
title: 'Bileşen API Tasarımı'
source: 'https://academia.sh/tr/kurslar/bilesen-tabanli-gelistirme/bilesen-api-tasarimi'
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şen API Tasarımı

Özellik yayılmasının birleştirme kuralları, sınıf ve dinleyicilerin birikmeli birleşimi, korunan alanlar, denetimli ile denetimsiz kip arasındaki sahiplik kararı ve özellik yüzeyinin geriye dönük uyumluluğu.

Bu konuda kurulan her şey bileşenin dış yüzeyine dayanıyordu: hangi özellikleri aldığı,
hangi deliği bıraktığı, hangi tutamağı verdiği. Yüzeyin kendisi henüz tasarlanmadı.

Dört soru açık duruyor. Çağıran bileşene tanımadığı bir öznitelik verdiğinde ne olacak?
Bileşenin kendi sınıf adıyla çağıranın verdiği sınıf adı çakışırsa hangisi kazanacak?
Bileşen bir olayı hem kendisi dinleyip hem çağıranın dinleyicisini çağırabilecek mi? Ve
gösterilen değerin sahibi bileşen mi, çağıran mı?

## Yayma Sırası

Bir bileşen, tanıdığı özellikleri kendi işine ayırıp geri kalanı ürettiği köke geçirebilir.
Buna **özellik yayılması (prop spreading)** denir ve çağırana bir kaçış deliği verir:
bileşenin düşünmediği bir öznitelik, bileşen değiştirilmeden eklenebilir.

Yayma tek başına yapıldığında sonuç, yazım sırasına bağlıdır ve birikmeli alanlar kaybolur.

```js
// api.mjs — ozellik yayilmasi, birlestirme kurallari ve yayma sirasi
const oge = (ad, ozn = {}, ...cocuklar) => ({ ad, ozn, cocuklar });

// Olayin kucuk modeli: iptal edilebilir varsayilan.
const olayYap = (tur) => ({ tur, varsayilanEngellendi: false, engelle() { this.varsayilanEngellendi = true; } });

// Iki islevi zincirle: once disaridan gelen, varsayilan engellenmediyse icteki.
const zincirle = (once, sonra) => (olay) => {
  once?.(olay);
  if (!olay.varsayilanEngellendi) sonra?.(olay);
};

// Birlestirme kurali: siniflar eklenir, bicem anahtar anahtar birlesir, duz oznitelikte
// sonuncu kazanir, dinleyicide ise SONUNCU ONCE calisir (cagiran bileseni iptal edebilsin).
function birlestir(...kumeler) {
  const sonuc = {};
  for (const kume of kumeler) {
    for (const [ad, deger] of Object.entries(kume ?? {})) {
      if (deger === undefined) continue;
      if (ad === "class") sonuc.class = [sonuc.class, deger].filter(Boolean).join(" ");
      else if (ad === "style") sonuc.style = { ...(sonuc.style ?? {}), ...deger };
      else if (/^on[A-Z]/.test(ad) && typeof deger === "function") {
        const onceki = sonuc[ad];
        sonuc[ad] = onceki ? zincirle(deger, onceki) : deger;
      } else sonuc[ad] = deger;
    }
  }
  return sonuc;
}

const kutuk = [];
const gelen = {
  class: "genis",
  style: { marginTop: "8px", color: "crimson" },
  onClick: () => kutuk.push("çağıranın dinleyicisi"),
  "data-izleme": "rozet-1",
  type: "submit",
};
const kendi = {
  class: "rozet-dugme",
  style: { color: "inherit", padding: "4px 8px" },
  onClick: () => kutuk.push("bileşenin kendi dinleyicisi"),
  type: "button",
};

// (a) Duz yayma: sonuncu kazanir, birikmeli alanlar kaybolur.
const duzOnce = { ...kendi, ...gelen };
const duzSonra = { ...gelen, ...kendi };
console.log("düz yayma, çağıran sonra :", JSON.stringify({ class: duzOnce.class, style: duzOnce.style, type: duzOnce.type }));
console.log("düz yayma, bileşen sonra :", JSON.stringify({ class: duzSonra.class, style: duzSonra.style, type: duzSonra.type }));

// (b) Birlestirme.
const birlesik = birlestir(kendi, gelen);
console.log("birleştirme              :", JSON.stringify({ class: birlesik.class, style: birlesik.style, type: birlesik.type }));

kutuk.length = 0;
duzOnce.onClick(olayYap("click"));
console.log("\ndüz yaymada çalışan dinleyiciler   :", JSON.stringify(kutuk));
kutuk.length = 0;
birlesik.onClick(olayYap("click"));
console.log("birleştirmede çalışan dinleyiciler:", JSON.stringify(kutuk));

// Cagiran varsayilani engellerse bilesenin dinleyicisi calismaz.
kutuk.length = 0;
const iptalEden = birlestir(kendi, { ...gelen, onClick: (o) => { kutuk.push("çağıran: iptal"); o.engelle(); } });
iptalEden.onClick(olayYap("click"));
console.log("çağıran iptal ettiğinde           :", JSON.stringify(kutuk));

// (c) Korunan alanlar: bilesenin dogrulugu tasiyan ozniteliklerini cagiran ezemez.
const KORUNAN = new Set(["role", "aria-controls", "id"]);
function guvenliBirlestir(kendi, gelen) {
  const suzulmus = Object.fromEntries(Object.entries(gelen).filter(([a]) => !KORUNAN.has(a)));
  const dusen = Object.keys(gelen).filter((a) => KORUNAN.has(a));
  return { ozellikler: birlestir(kendi, suzulmus), dusen };
}
const { ozellikler, dusen } = guvenliBirlestir(
  { role: "tab", id: "suzgec-baslik-esik", class: "sekme", "aria-controls": "suzgec-govde-esik" },
  { role: "button", id: "benim-kimligim", "aria-controls": "baska-govde", class: "vurgulu", title: "Eşik ayarları" }
);
console.log("\nkorunan alanlar düşürüldü:", JSON.stringify(dusen));
console.log("sonuç:", JSON.stringify(ozellikler));
console.log("üretilen düğüm:", JSON.stringify(oge("button", ozellikler).ozn));
```

```
düz yayma, çağıran sonra : {"class":"genis","style":{"marginTop":"8px","color":"crimson"},"type":"submit"}
düz yayma, bileşen sonra : {"class":"rozet-dugme","style":{"color":"inherit","padding":"4px 8px"},"type":"button"}
birleştirme              : {"class":"rozet-dugme genis","style":{"color":"crimson","padding":"4px 8px","marginTop":"8px"},"type":"submit"}

düz yaymada çalışan dinleyiciler   : ["çağıranın dinleyicisi"]
birleştirmede çalışan dinleyiciler: ["çağıranın dinleyicisi","bileşenin kendi dinleyicisi"]
çağıran iptal ettiğinde           : ["çağıran: iptal"]

korunan alanlar düşürüldü: ["role","id","aria-controls"]
sonuç: {"role":"tab","id":"suzgec-baslik-esik","class":"sekme vurgulu","aria-controls":"suzgec-govde-esik","title":"Eşik ayarları"}
üretilen düğüm: {"role":"tab","id":"suzgec-baslik-esik","class":"sekme vurgulu","aria-controls":"suzgec-govde-esik","title":"Eşik ayarları"}
```

İlk iki satır iki yönlü kaybı gösteriyor. Çağıran sonra yayıldığında bileşenin kendi
sınıfı ve `type` değeri siliniyor; düğme, çağıranın hiç istemediği bir gönderim düğmesine
dönüşüyor. Bileşen sonra yayıldığında bu kez çağıranın verdiği hiçbir şey kalmıyor ve kaçış
deliği kapanıyor.

Üçüncü satır birleştirmenin sonucunu veriyor. Üç alan üç ayrı kuralla işleniyor. Sınıf
**eklenir**: bileşenin sınıfı önde, çağıranınki arkada; CSS basamaklamasında sıra
belirleyici olmadığı için ikisi de kuralda kalır. Biçem **anahtar anahtar birleşir**:
çağıranın verdiği `color` bileşeninkini eziyor, `padding` bileşenin verdiği gibi kalıyor,
`marginTop` yalnızca çağıranda var. Düz öznitelikte **sonuncu kazanır**: `type` çağıranın
istediği gibi.

## Dinleyicilerin Zincirlenmesi

Dinleyiciler için tek bir değer seçmek her zaman yanlıştır: bileşenin kendi davranışı da,
çağıranın isteği de çalışmalıdır. Çıktının ikinci bölümü düz yaymada bileşenin dinleyicisinin
tümüyle kaybolduğunu, birleştirmede ise ikisinin de çalıştığını gösteriyor.

Zincirin **sırası** bir tasarım kararıdır ve düz özniteliklerin kuralının tersidir. Çağıranın
dinleyicisi önce çalışır; varsayılanı engellerse bileşenin kendi davranışı hiç çalışmaz. Son
satır bunu gösteriyor: yalnızca çağıranın dinleyicisi çalışmış, bileşeninki atlanmış. Ters
sıra seçilseydi çağıran bileşeni durduramazdı, yalnızca ondan sonra bir şey ekleyebilirdi.

Tarayıcı ve Web Platformu kursundaki Varsayılan Davranış ve İptal dersinde tanımlanan
iptal edilebilirlik burada bileşen düzeyine taşınmış oluyor: bileşenin davranışı, çağıranın
iptal edebileceği bir varsayılan gibi ele alınıyor.

## Korunan Alanlar

Kaçış deliği sınırsız olamaz. Çıktının son bölümünde çağıran `role`, `id` ve `aria-controls`
değerlerini değiştirmeye çalışıyor; üçü de düşürülüyor.

Ayrım, ilk derste kurulan ölçütle aynıdır. `title` bir görünüm kararıdır ve geçer. `role`
ise bileşenin doğruluğunun parçasıdır: bir sekme başlığının rolü değiştirilirse başlıkla
gövde arasındaki ilişki anlamını yitirir. `aria-controls` ve `id` de bileşenin kendi
ürettiği ilişkiyi taşır; çağıranın verdiği değer bu ilişkiyi koparır.

Korunan alanların **sessizce** düşürülmesi de doğru değildir; geliştirme kipinde bir uyarı
yazılması, çağıranın kaçış deliğinin nerede bittiğini öğrenmesini sağlar.

## Değerin Sahibi

Son soru en sık kusur üretenidir: bileşenin gösterdiği değeri kim tutar?

```js
// denetim.mjs — degerin sahibi kim: denetimli ve denetimsiz kip
function alanOrnegi() {
  let icDurum;
  let kip = null;
  const uyarilar = [];
  return {
    uyarilar,
    ciz({ deger, ilkDeger = -5, degisti }) {
      const denetimli = deger !== undefined;
      if (kip === null) { kip = denetimli ? "denetimli" : "denetimsiz"; if (!denetimli) icDurum = ilkDeger; }
      else if ((kip === "denetimli") !== denetimli) {
        uyarilar.push(`kip değişti: ${kip} → ${denetimli ? "denetimli" : "denetimsiz"}`);
        kip = denetimli ? "denetimli" : "denetimsiz";
        if (!denetimli) icDurum = ilkDeger;
      }
      const gosterilen = denetimli ? deger : icDurum;
      return {
        kip, gosterilen,
        yaz(yeni) { if (!denetimli) icDurum = yeni; degisti?.(yeni); },
      };
    },
  };
}

const satir = (etiket, g) => console.log(`${etiket.padEnd(34)} ${String(g).padStart(6)}`);

console.log("--- A. denetimsiz: deger bilesenin ---");
const a = alanOrnegi();
let g = a.ciz({ ilkDeger: -5 });
satir("ilk çizim (ilkDeger=-5)", g.gosterilen);
g.yaz(12);
g = a.ciz({ ilkDeger: -5 });
satir("kullanıcı 12 yazdıktan sonra", g.gosterilen);
g = a.ciz({ ilkDeger: 20 });
satir("ilkDeger 20 olarak verildiğinde", g.gosterilen);

console.log("\n--- B. denetimli: deger cagiranin, degisti bagli ---");
const b = alanOrnegi();
let ustDurum = -5;
const bCiz = () => b.ciz({ deger: ustDurum, degisti: (v) => { ustDurum = v; } });
g = bCiz();
satir("ilk çizim (deger=-5)", g.gosterilen);
g.yaz(12);
g = bCiz();
satir("kullanıcı 12 yazdıktan sonra", g.gosterilen);
ustDurum = 30;
g = bCiz();
satir("üst durum 30'a çekildiğinde", g.gosterilen);

console.log("\n--- C. denetimli ama degisti yok: donmus alan ---");
const c = alanOrnegi();
const cCiz = () => c.ciz({ deger: -5 });
g = cCiz();
satir("ilk çizim (deger=-5)", g.gosterilen);
g.yaz(12);
g = cCiz();
satir("kullanıcı 12 yazdıktan sonra", g.gosterilen);

console.log("\n--- D. kip degisimi ---");
const d = alanOrnegi();
d.ciz({ ilkDeger: -5 });
d.ciz({ deger: 7, degisti: () => {} });
d.ciz({ ilkDeger: -5 });
console.log("uyarılar:", JSON.stringify(d.uyarilar));

// Iki kipi tek imzada barindiran bilesenin sozlesmesi
console.log("\nözellik durumu             kip          değeri kim tutar  değişimi kim bildirir");
for (const [deger, degisti] of [[undefined, undefined], [undefined, "var"], [-5, undefined], [-5, "var"]]) {
  const denetimli = deger !== undefined;
  console.log(
    `${(`deger=${deger ?? "yok"}, değişti=${degisti ?? "yok"}`).padEnd(26)} ` +
    `${(denetimli ? "denetimli" : "denetimsiz").padEnd(12)} ` +
    `${(denetimli ? "çağıran" : "bileşen").padEnd(17)} ` +
    `${degisti ? "çağıran dinler" : denetimli ? "kimse — değer donar" : "kimse"}`
  );
}
```

```
--- A. denetimsiz: deger bilesenin ---
ilk çizim (ilkDeger=-5)                -5
kullanıcı 12 yazdıktan sonra           12
ilkDeger 20 olarak verildiğinde        12

--- B. denetimli: deger cagiranin, degisti bagli ---
ilk çizim (deger=-5)                   -5
kullanıcı 12 yazdıktan sonra           12
üst durum 30'a çekildiğinde            30

--- C. denetimli ama degisti yok: donmus alan ---
ilk çizim (deger=-5)                   -5
kullanıcı 12 yazdıktan sonra           -5

--- D. kip degisimi ---
uyarılar: ["kip değişti: denetimsiz → denetimli","kip değişti: denetimli → denetimsiz"]

özellik durumu             kip          değeri kim tutar  değişimi kim bildirir
deger=yok, değişti=yok     denetimsiz   bileşen           kimse
deger=yok, değişti=var     denetimsiz   bileşen           çağıran dinler
deger=-5, değişti=yok      denetimli    çağıran           kimse — değer donar
deger=-5, değişti=var      denetimli    çağıran           çağıran dinler
```

**Denetimsiz kip** değeri bileşene bırakır. Çağıran yalnızca başlangıcı verir; sonrası
bileşenin işidir. A bölümünün son satırı bu kipin en çok şaşırtan yanını gösteriyor:
`ilkDeger` 20 yapıldığı hâlde gösterilen değer 12 kalıyor. "İlk" sözcüğü sözleşmenin
kendisidir — özellik yalnızca ilk çizimde okunur. Adında "ilk" geçmeyen bir özellik bu
davranışı taşıyamaz.

**Denetimli kip** değeri çağırana bırakır. B bölümünde bileşen hiçbir değer tutmuyor;
gösterilen şey her zaman gelen özelliktir. Üst durum bileşenden bağımsız değiştiğinde
görünüm de değişiyor — denetimli kipi seçmenin nedeni budur, çünkü değerin başka bir yerden
de değişmesi gerekebilir.

C bölümü tek bir eksiğin ne yaptığını gösteriyor. Değer verilmiş, bildirim işlevi
verilmemiş; kullanıcı yazıyor, hiçbir şey olmuyor. Alan donmuş görünüyor ve bunun nedeni
kaynak metinde bir satır uzağa yazılmış. Bileşen bu birleşimi geliştirme kipinde
bildirmelidir.

D bölümü ikinci tuzağı gösteriyor: aynı bileşen örneği kip değiştirdiğinde, değerin sahibi
ömrün ortasında değişir. Bu, genellikle üst durumun ilk çizimde tanımsız olmasından doğar.
Sözleşme, kipin örneğin ömrü boyunca sabit olmasını ister.

## Yüzeyin Kararlılığı

Bileşenin özellik yüzeyi bir sözleşmedir ve sözleşme değişiklikleri iki sınıfa ayrılır.

Varsayılanı olan yeni bir özellik eklemek uyumludur; hiçbir çağıran etkilenmez. Var olan bir
özelliğin varsayılanını değiştirmek uyumlu **değildir**: kaynak metnini hiç değiştirmemiş
her çağıran farklı davranış görür. Bir özelliğin adını değiştirmek de uyumsuzdur, ama en
azından gürültülüdür — eski ad artık okunmaz ve etkisi hemen görülür.

Yüzeyin biçimi de kararlılığı etkiler. İlk derste görüldüğü gibi bağımsız mantıksal
özellikler üstel bir birleşim kümesi üretir; birbirini dışlayan seçenekler tek bir
adlandırılmış özellikte toplandığında hem küme küçülür hem de yeni bir seçenek eklemek
uyumlu bir değişiklik olur. Aynı ölçüt geri bildirim işlevleri için de geçerlidir: tek
değer alan bir işlev yerine adlandırılmış alanları olan bir nesne almak, ileride alan
eklenmesine izin verir.

## Özet

- Özellik yayılması çağırana kaçış deliği verir; düz yayma ise yazım sırasına göre ya
  bileşenin ya çağıranın verdiklerini siler.
- Birleştirmenin üç kuralı vardır: sınıf eklenir, biçem anahtar anahtar birleşir, düz
  öznitelikte sonuncu kazanır.
- Dinleyiciler zincirlenir ve zincirin sırası düz özniteliklerin tersidir: çağıranın
  dinleyicisi önce çalışır, varsayılanı engellerse bileşenin davranışı çalışmaz.
- Bileşenin doğruluğunu taşıyan alanlar korunur; düşürülen alan geliştirme kipinde
  bildirilir.
- Denetimsiz kipte değer bileşenin, denetimli kipte çağıranındır; "ilk" ön eki taşıyan
  özellik yalnızca ilk çizimde okunur, kip örneğin ömrü boyunca sabit kalmalıdır ve değer
  verilip bildirim işlevi verilmemesi alanı dondurur.
- Varsayılanı olan yeni özellik eklemek uyumlu, var olan varsayılanı değiştirmek uyumsuz
  bir değişikliktir.

## Sonraki Adım

Bu konu boyunca bileşenler kâğıt üzerinde birleştirildi: bir işlev bir ağaç üretti, ağaçlar
iç içe geçti, özellikler birleşti. Üretilen ağacın belgeye nasıl yansıdığına hiç
bakılmadı — durum değiştiğinde hangi düğümlerin dokunulduğu, ne kadar karşılaştırma
yapıldığı ve bu işin kimin yaptığı açık bırakıldı. Bu soruların yanıtı bileşim kalıplarına
değil, çerçevenin güncelleme modeline bağlıdır ve aileden aileye değişir. Sonraki konu, bu
modelleri özellikleriyle ele alır ve ilk ailede, durumdan yeni bir ağaç üretip onu
öncekiyle karşılaştıran yaklaşımda, bir hücrelik değişikliğin kaç birim iş ürettiğini sayar.
