---
title: 'Özel Kancalar ve Birleştirilebilir İşlevler'
source: 'https://academia.sh/tr/kurslar/bilesen-tabanli-gelistirme/ozel-kancalar-ve-birlestirilebilir-islevler'
course: 'Bileşen Tabanlı Arayüz Geliştirme'
language: tr
updated: '2026-08-17T18:09:13+00:00'
license: 'CC BY-SA 4.0'
---

# Özel Kancalar ve Birleştirilebilir İşlevler

Durumu ve yan etkiyi işaretleme üretmeden paketleyen birimler; konum tabanlı kancaların çağrı sırası kuralı, kurulumda bir kez çalışan işlevlerin kapsam temizliği ve iki modelin karşılaştırması.

Önceki ders yapının yeniden kullanımını çözdü. Ancak bileşenlerde tekrar eden tek şey yapı
değildir. Kuzey Yamaç Ölçüm İstasyonu sayfasında süzgeç panelinin arama alanı girdiyi
geciktirerek tetikliyor, ölçüm tablosu sıralama tercihini tutuyor, ölçüm rozeti aralıklı
olarak yenileniyor. Üçünde de durum var, üçünde de bir yan etki ve o etkinin temizliği var.
Üçü de ayrı ayrı yazılmış.

Bu mantık bileşenin gövdesinde durduğu sürece taşınamaz. Taşınabilmesi için işaretlemeden
ayrılması, ayrılabilmesi içinse durumun bir bileşene değil bir **çağrıya** bağlanabilmesi
gerekir. Bu dersin konusu, bu çağrının iki farklı çerçeve ailesinde nasıl kurulduğu ve
her birinin hangi kuralı dayattığıdır.

## Ayrılan Nedir

Bir bileşen iki iş yapar: durumu yönetir ve o durumdan bir ağaç üretir. İkinci iş
işaretlemedir ve bileşene özgüdür. Birinci iş çoğu zaman özgü değildir.

Ayırma işlemi şu biçimi alır: durumu, türetilmiş değerleri ve yan etkileri tutan; işaretleme
**üretmeyen**; dışarıya okunacak değerlerle çağrılacak işlevlerden oluşan bir tutamak
döndüren bir işlev. Sanal ağaç tutan çerçeve ailesinde bu işleve **özel kanca (custom hook)**,
bağımlılığı çalışma zamanında izleyen ailede **birleştirilebilir işlev (composable)** denir.
Amaçları aynı, çalışma zamanı sözleşmeleri farklıdır.

Ayrımın sınavı basit bir sorudur: bu işlevi çağıran iki bileşen tümüyle farklı görünebiliyor
mu? Yanıt hayırsa ayrılan şey mantık değil, gizlenmiş bir bileşendir.

## Konuma Bağlı Yuvalar

Sanal ağaç ailesinde bileşen bir işlevdir ve her çizimde baştan çalışır. Bir çizimde
oluşturulan yerel değişkenler sonraki çizime kalmaz. Durumun bileşenin dışında, çerçevenin
tuttuğu bir yerde yaşaması gerekir.

Bunun en ucuz gerçekleştirimi konumdur: bileşene ait bir yuva dizisi tutulur, her kanca
çağrısı bir sonraki yuvayı alır. Kancanın kimliği adı ya da parametresi değil, **kaçıncı
çağrı olduğudur**.

```js
// kanca.mjs — konum tabanli kanca calisma zamani, kanca bilesimi ve cagri sirasi kurali
let yuvalar = [];
let imlec = 0;

// Bir cizim: imleci sifirla, bilesen islevini calistir.
const ciz = (bilesen, ozellikler = {}) => { imlec = 0; return bilesen(ozellikler); };

// Tek ilkel: konuma bagli durum yuvasi.
function durumKullan(ilkDeger) {
  const i = imlec++;
  if (!(i in yuvalar)) yuvalar[i] = ilkDeger;
  return [yuvalar[i], (yeni) => { yuvalar[i] = yeni; }];
}

// Ozel kanca: iki yuva ve bir dogrulama kurali; isaretleme uretmez.
function esikKullan(baslangic) {
  const [esik, esikAyarla] = durumKullan(baslangic);
  const [dokunuldu, dokunulduAyarla] = durumKullan(false);
  const gecerli = Number.isFinite(esik) && esik >= -40 && esik <= 40;
  return {
    esik, gecerli,
    hata: dokunuldu && !gecerli ? "eşik -40 ile 40 arasında olmalı" : null,
    yaz(yeni) { esikAyarla(yeni); dokunulduAyarla(true); },
  };
}

// Ikinci ozel kanca: siralama alani ve yonu.
function siralamaKullan(ilkAlan) {
  const [alan, alanAyarla] = durumKullan(ilkAlan);
  const [artan, artanAyarla] = durumKullan(true);
  return {
    alan, artan,
    tikla(yeni) { if (yeni === alan) artanAyarla(!artan); else { alanAyarla(yeni); artanAyarla(true); } },
  };
}

// Ucuncu ozel kanca yalnizca ilk ikisini birlestirir; kendi yuvasi yoktur.
const suzgecKullan = () => ({ esik: esikKullan(-5), siralama: siralamaKullan("ad") });

// Bilesen: kancayi cagirir, gorunumunu ve disariya verdigi tutamagi dondurur.
function suzgecPaneli() {
  const s = suzgecKullan();
  const ozet =
    `eşik=${s.esik.esik} geçerli=${s.esik.gecerli} hata=${s.esik.hata ?? "-"} ` +
    `sıralama=${s.siralama.alan}/${s.siralama.artan ? "artan" : "azalan"}`;
  return { ozet, s };
}

console.log("--- iki ozel kancanin bilesimi: dort yuva ---");
const c1 = ciz(suzgecPaneli);
console.log("1. çizim:", c1.ozet, "| yuvalar:", JSON.stringify(yuvalar));

c1.s.esik.yaz(88);
const c2 = ciz(suzgecPaneli);
console.log("2. çizim:", c2.ozet, "| yuvalar:", JSON.stringify(yuvalar));

c2.s.siralama.tikla("deger");
const c3 = ciz(suzgecPaneli);
console.log("3. çizim:", c3.ozet, "| yuvalar:", JSON.stringify(yuvalar));

c3.s.siralama.tikla("deger");
const c4 = ciz(suzgecPaneli);
console.log("4. çizim:", c4.ozet, "| yuvalar:", JSON.stringify(yuvalar));

// --- Cagri sirasi kurali ---
console.log("\n--- kosullu cagri: yuvalarin kaymasi ---");
yuvalar = [];
function bozukPanel({ gelismis }) {
  const [sorgu] = durumKullan("kar");
  if (gelismis) durumKullan(-5);            // yalnizca bazi cizimlerde cagriliyor
  const [siralama] = durumKullan("ad");
  return { sorgu, siralama };
}
console.log("gelişmiş açık  :", JSON.stringify(ciz(bozukPanel, { gelismis: true })),
  "| yuvalar:", JSON.stringify(yuvalar));
console.log("gelişmiş kapalı:", JSON.stringify(ciz(bozukPanel, { gelismis: false })),
  "| yuvalar:", JSON.stringify(yuvalar));

// Kurali uygulayan surum: kanca kosulsuz cagrilir, kosul dondurulen degerdedir.
console.log("\n--- kosulsuz cagri, kosullu deger ---");
yuvalar = [];
function saglamPanel({ gelismis }) {
  const [sorgu] = durumKullan("kar");
  const [esik] = durumKullan(-5);
  const [siralama] = durumKullan("ad");
  return { sorgu, esik: gelismis ? esik : null, siralama };
}
console.log("gelişmiş açık  :", JSON.stringify(ciz(saglamPanel, { gelismis: true })),
  "| yuvalar:", JSON.stringify(yuvalar));
console.log("gelişmiş kapalı:", JSON.stringify(ciz(saglamPanel, { gelismis: false })),
  "| yuvalar:", JSON.stringify(yuvalar));
```

```
--- iki ozel kancanin bilesimi: dort yuva ---
1. çizim: eşik=-5 geçerli=true hata=- sıralama=ad/artan | yuvalar: [-5,false,"ad",true]
2. çizim: eşik=88 geçerli=false hata=eşik -40 ile 40 arasında olmalı sıralama=ad/artan | yuvalar: [88,true,"ad",true]
3. çizim: eşik=88 geçerli=false hata=eşik -40 ile 40 arasında olmalı sıralama=deger/artan | yuvalar: [88,true,"deger",true]
4. çizim: eşik=88 geçerli=false hata=eşik -40 ile 40 arasında olmalı sıralama=deger/azalan | yuvalar: [88,true,"deger",false]

--- kosullu cagri: yuvalarin kaymasi ---
gelişmiş açık  : {"sorgu":"kar","siralama":"ad"} | yuvalar: ["kar",-5,"ad"]
gelişmiş kapalı: {"sorgu":"kar","siralama":-5} | yuvalar: ["kar",-5,"ad"]

--- kosulsuz cagri, kosullu deger ---
gelişmiş açık  : {"sorgu":"kar","esik":-5,"siralama":"ad"} | yuvalar: ["kar",-5,"ad"]
gelişmiş kapalı: {"sorgu":"kar","esik":null,"siralama":"ad"} | yuvalar: ["kar",-5,"ad"]
```

İlk bölüm bileşimin çalıştığını gösteriyor. `suzgecKullan` kendi yuvası olmayan bir
kancadır; yalnızca iki kancayı yan yana çağırır ve dördüncü çizime kadar dört yuva sırayla
doğru değerleri taşır. Yuva dizisi düz bir listedir: iç içe kanca çağrılarının hiyerarşisi
dizide görünmez, yalnızca çağrı sırası görünür.

Kancaların birbirini çağırabilmesi bu modelin gücüdür. Bir kanca içeride kaç yuva
kullandığını dışarıya bildirmez; çağıranın bilmesi de gerekmez, yeter ki sıra sabit kalsın.

## Çağrı Sırası Kuralı

İkinci bölüm sabitliğin bozulduğunda ne olduğunu gösteriyor. `gelismis` doğruyken üç kanca
çağrılıyor ve yuvalar `["kar", -5, "ad"]` oluyor. Aynı bileşen `gelismis` yanlışken
çizildiğinde ortadaki çağrı atlanıyor; sıralama kancası artık ikinci sırada ve **birinci
kancanın yuvası olan** 1. dizini okuyor. Sonuç, sıralama alanının `-5` sayısı olması.

Hata sessizdir. Bir istisna atılmaz, bir uyarı yazılmaz; yalnızca yanlış değer okunur.
Ölçüm tablosunda bu, sıralamanın sessizce bozulması demektir.

Buradan konum tabanlı çalışma zamanının tek kuralı çıkar: **kancalar her çizimde aynı sayıda
ve aynı sırada çağrılmalıdır.** Koşula, döngüye ya da erken dönüşe konamazlar. Üçüncü bölüm
kuralın nasıl uygulandığını gösteriyor: kanca koşulsuz çağrılır, koşul döndürülen değere
taşınır. Yuva dizisi her iki çizimde de aynı kalıyor.

Kuralın bir bedeli var: kullanılmayan durum yine de tutulur. `gelismis` kapalıyken eşik
yuvası bellekte durur ve değerini korur. Bu, çoğu zaman istenen davranıştır — kullanıcı
gelişmiş paneli kapatıp açtığında eşik yerinde durur — ama bir tercih değil, modelin
zorunlu sonucudur.

## Kurulumda Bir Kez Çalışan İşlevler

Bağımlılığı çalışma zamanında izleyen ailede bileşen işlevi her güncellemede yeniden
çalışmaz; bir kez çalışır, tepkisel değerleri kurar ve görünümü bu değerlere bağlar.
Durumun kimliği bir yuva sırası değil, kurulumda üretilen nesnenin kendisidir.

```js
// birlestirilebilir.mjs — kurulum sirasinda bir kez calisan islevler ve kapsam temizligi
let etkinKapsam = null;

function kapsamAc(kurulum) {
  const temizlikler = [];
  const onceki = etkinKapsam;
  etkinKapsam = { temizlikEkle: (f) => temizlikler.push(f) };
  const sonuc = kurulum();
  etkinKapsam = onceki;
  return {
    sonuc,
    yikil() {                                   // son kurulan ilk yikilir
      for (let i = temizlikler.length - 1; i >= 0; i--) temizlikler[i]();
      temizlikler.length = 0;
    },
    bekleyenTemizlik: () => temizlikler.length,
  };
}

// Konumdan bagimsiz durum: kimlik yuvanin sirasi degil, nesnenin kendisidir.
function durum(ilkDeger) {
  let deger = ilkDeger;
  const dinleyiciler = new Set();
  return {
    oku: () => deger,
    yaz(yeni) { deger = yeni; for (const d of [...dinleyiciler]) d(yeni); },
    dinle(d) {
      dinleyiciler.add(d);
      etkinKapsam?.temizlikEkle(() => dinleyiciler.delete(d));
    },
    dinleyiciSayisi: () => dinleyiciler.size,
  };
}

const kutuk = [];

// Birlestirilebilir islev 1: esik ve dogrulamasi.
function esikKullan(baslangic) {
  const esik = durum(baslangic);
  const gecerli = () => Number.isFinite(esik.oku()) && esik.oku() >= -40 && esik.oku() <= 40;
  esik.dinle((v) => kutuk.push(`eşik yazıldı: ${v} (geçerli=${gecerli()})`));
  return { esik, gecerli };
}

// Birlestirilebilir islev 2: sahte zamanlayiciyla aralikli yenileme.
const saat = { gorevler: [], her: (ad, is) => { saat.gorevler.push({ ad, is }); return () => { saat.gorevler = saat.gorevler.filter((g) => g.is !== is); }; }, tik: () => saat.gorevler.forEach((g) => g.is()) };

function yenilemeKullan(ad) {
  const sayac = durum(0);
  const durdur = saat.her(ad, () => sayac.yaz(sayac.oku() + 1));
  etkinKapsam?.temizlikEkle(() => { durdur(); kutuk.push(`${ad}: yenileme durduruldu`); });
  return sayac;
}

// Birlestirilebilir islev 3: yalnizca digerlerini birlestirir.
function panelKurulumu({ gelismis }) {
  const esikDurumu = esikKullan(-5);
  // Kosullu kurulum guvenlidir: kimlik konuma bagli degil.
  const yenileme = gelismis ? yenilemeKullan("gelişmiş panel") : null;
  return { ...esikDurumu, yenileme };
}

console.log("--- gelismis kapali ---");
const a = kapsamAc(() => panelKurulumu({ gelismis: false }));
a.sonuc.esik.yaz(12);
saat.tik();
console.log("yenileme nesnesi:", a.sonuc.yenileme, "| saatteki görev:", saat.gorevler.length,
  "| bekleyen temizlik:", a.bekleyenTemizlik());

console.log("\n--- gelismis acik ---");
const b = kapsamAc(() => panelKurulumu({ gelismis: true }));
b.sonuc.esik.yaz(88);
saat.tik(); saat.tik();
console.log("yenileme sayacı:", b.sonuc.yenileme.oku(), "| saatteki görev:", saat.gorevler.length,
  "| bekleyen temizlik:", b.bekleyenTemizlik());
console.log("eşik dinleyicisi:", b.sonuc.esik.dinleyiciSayisi());

console.log("\n--- kapsam yikildiktan sonra ---");
b.yikil();
saat.tik();
console.log("saatteki görev:", saat.gorevler.length,
  "| eşik dinleyicisi:", b.sonuc.esik.dinleyiciSayisi(),
  "| yenileme sayacı:", b.sonuc.yenileme.oku());

console.log("\nkütük:");
for (const s of kutuk) console.log(" ", s);
```

```
--- gelismis kapali ---
yenileme nesnesi: null | saatteki görev: 0 | bekleyen temizlik: 1

--- gelismis acik ---
yenileme sayacı: 2 | saatteki görev: 1 | bekleyen temizlik: 2
eşik dinleyicisi: 1

--- kapsam yikildiktan sonra ---
saatteki görev: 0 | eşik dinleyicisi: 0 | yenileme sayacı: 2

kütük:
  eşik yazıldı: 12 (geçerli=true)
  eşik yazıldı: 88 (geçerli=false)
  gelişmiş panel: yenileme durduruldu
```

Koşullu kurulum burada güvenlidir. `gelismis` kapalıyken yenileme işlevi hiç çağrılmaz,
hiçbir yuva kaymaz, hiçbir değer bozulmaz — çünkü kimlik sıraya değil nesneye bağlıdır.
Kapalı durumda saatte hiç görev yoktur; açık durumda bir görev vardır ve iki tikten sonra
sayaç 2'dir.

İkinci ayrım temizliktedir. Birleştirilebilir işlevler kendi temizliklerini kurulum
sırasındaki **etkin kapsama** kaydeder; çağıran bunu bilmez ve elle bir şey yapmaz. Kapsam
yıkıldığında kayıtlar ters sırada çalışır: son kurulan ilk yıkılır. Yıkımdan sonra saatte
görev, eşikte dinleyici kalmaz; sayaç son değerinde donar.

Ters sıra bir ayrıntı değildir. İkinci işlev birincinin kurduğu şeye yaslanıyorsa, yıkım
sırasında birinci hâlâ ayakta olmalıdır. Kurulum sırasının tersi bu koşulu her zaman sağlar.

## İki Modelin Ödünleşimi

İki model aynı amacı farklı bedellerle karşılar.

Konum tabanlı model çalışma zamanında hiçbir defter tutmaz: yuva dizisi ve bir imleç
yeter. Bedeli, çağrı sırasının bir sözleşmeye dönüşmesi ve bu sözleşmenin dilin kendisi
tarafından denetlenememesidir. Kural, insan disiplinine ve statik çözümlemeye bırakılır.

Kurulum tabanlı model çağrı sırasına hiç bağımlı değildir; koşullu, döngü içinde, gecikmeli
kurulum yapılabilir. Bedeli, her tepkisel değerin ve her dinleyicinin bir nesne olarak
tutulmasıdır: kayıt defteri çalışma zamanındadır. Ayrıca "kurulum bir kez çalışır" kuralı
kendi tuzağını getirir — kurulumda okunan bir özellik, o özelliğin ilk değerini yakalar;
sonraki değerleri izlemek için okunan değerin kendisinin tepkisel olması gerekir.

Her iki modelde de üç kural ortaktır. Ayrılan işlev işaretleme döndürmez; döndürseydi o bir
bileşen olurdu. Aldığı ve döndürdüğü şeyler adlandırılmış olmalıdır; iki elemanlı bir dizi
döndüren kanca, üçüncü değer eklendiğinde her çağıranı bozar. Ve ayrılan işlev tek başına
sınanabilmelidir: bileşen çizmeden çağrılıp değerleri okunabiliyorsa ayrım gerçekten
yapılmıştır.

## Özet

- Özel kanca ve birleştirilebilir işlev, durumu ve yan etkiyi işaretleme üretmeden paketleyen
  birimlerdir; iki çerçeve ailesinde aynı amacın farklı sözleşmeleridir.
- Konum tabanlı çalışma zamanında kancanın kimliği çağrı sırasıdır; yuva dizisi düzdür ve
  iç içe kanca çağrılarının hiyerarşisini taşımaz.
- Koşullu bir kanca çağrısı yuvaları kaydırır ve hata sessizdir: istisna atılmaz, yanlış
  değer okunur. Kural, kancanın koşulsuz çağrılıp koşulun döndürülen değere taşınmasıdır.
- Kurulumda bir kez çalışan işlevlerde kimlik nesnedir; koşullu kurulum güvenlidir, bedeli
  çalışma zamanında tutulan kayıt defteridir.
- Temizlik etkin kapsama kaydedilir ve yıkım kurulumun tersi sırada yapılır; böylece bir
  işlevin yaslandığı şey o işlev yıkılırken hâlâ ayaktadır.
- Ayrılan işlev işaretleme döndürmez, adlandırılmış değerler döndürür ve bileşen çizmeden
  sınanabilir.

## Sonraki Adım

Bu ders mantığı bileşenden çıkardı, ama bileşenler arasındaki ilişkiyi ele almadı. Süzgeç
panelinde sekmeli bir yapı var: sekme başlıkları, sekme gövdeleri ve hangi sekmenin etkin
olduğu bilgisi. Bu bilgiyi her sekmeye özellik olarak vermek, önceki derste ölçülen özellik
zincirini geri getirir; her sekmenin kendi kopyasını tutması ise iki sekmenin aynı anda
etkin olmasına izin verir. İhtiyaç duyulan şey, ayrı bileşenler olarak yazılmış ama tek bir
durumu paylaşan bir ailedir. Sonraki ders bu ailenin nasıl kurulduğunu, aralarındaki örtük
sözleşmenin nasıl denetlendiğini ve yanlış kullanımın nasıl sessiz kalmaktan çıkarıldığını
gösterir.
