---
title: 'Liste Başarımı'
source: 'https://academia.sh/tr/kurslar/react-native/liste-basarimi'
course: 'React Native'
language: tr
updated: '2026-10-21T05:02:55+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Başa öge eklenen bir listede sıraya göre eşlemenin satırlara verdiği akıbeti ölçer'
    - 'Kararlı anahtarın aynı eklemeyi tek bir kurulan düğüme indirdiğini gösterir'
    - 'İki eşleme biçiminin komut sayılarını karşılaştırır'
    - 'Sanallaştırılmış listenin pencere mantığını ağaçtaki satır sayısından ayırır'
---

# Liste Başarımı

Bir listenin başına öge eklenince satır düğümlerine ne olduğunu ölçer. Sıraya göre eşlemede satırların içeriği kayar; kararlı anahtarla aynı ekleme tek bir kurulan düğüm verir.

Önceki derste animasyon yerel tarafta sürdü. İlerleyen değer ağaca özellik olarak girmediği için düğüm korunan kaldı. Liste bunun tersi bir yerdir: değişen şey doğrudan ağaçtaki ögelerin kendisidir ve tek bir ekleme, bütün satırların akıbetini etkileyebilir. Bu ders listenin ne kadar hızlı kaydığını sormaz. Sorduğu şey, bir öge eklenince hangi satır düğümüne ne olduğudur.

Kursun sorusu aynıdır: **bu güncelleme görünüm ağacındaki düğüme ne yapıyor?** Yanıtın dört biçimi var. Düğüm yerinde kalır ve özellikleri aynıysa **korunan**, özellikleri değiştiyse **güncellenen** olur. Önceki ağaçta karşılığı olmayan düğüm **kurulan**, yeni ağaçta karşılığı kalmayan düğüm **sökülen** olur. Bir güncellemenin yerel tarafa gönderdiği iş, korunan dışındaki üç akıbetin toplamıdır ve bu toplam **komut** diye anılır. Akıbeti etiket değil aşağıdaki kural verir. Ağacın durumdan yeniden üretilmesine yeniden çizim (render) denir. Kural, iki ağacı çocuklarını anahtarla ya da anahtar yoksa sırayla eşleyerek karşılaştırır.

```javascript
// fate.mjs — bir guncellemenin gorunum agacindaki her dugume ne yaptigini siniflandirir
export const FATES = ["korunan", "guncellenen", "kurulan", "sokulen"];

export function el(type, props = {}, children = [], key = null) {
  return { type, props, children, key };
}

function segment(node) {
  return node.key === null ? node.type : `${node.type}[${node.key}]`;
}

function identity(node, index) {
  return node.key === null ? `#${index}` : `key:${node.key}`;
}

function sameProps(a, b) {
  const names = [...new Set([...Object.keys(a), ...Object.keys(b)])].sort();
  return names.every((n) => JSON.stringify(a[n]) === JSON.stringify(b[n]));
}

function whole(node, path, fate, ops) {
  const here = `${path}/${segment(node)}`;
  ops.push({ fate, path: here });
  node.children.forEach((child) => whole(child, here, fate, ops));
}

export function reconcile(prev, next, path = "", ops = []) {
  if (prev === null) {
    whole(next, path, "kurulan", ops);
    return ops;
  }
  if (next === null) {
    whole(prev, path, "sokulen", ops);
    return ops;
  }
  if (prev.type !== next.type || prev.key !== next.key) {
    whole(prev, path, "sokulen", ops);
    whole(next, path, "kurulan", ops);
    return ops;
  }
  const here = `${path}/${segment(next)}`;
  ops.push({ fate: sameProps(prev.props, next.props) ? "korunan" : "guncellenen", path: here });
  const before = new Map(prev.children.map((child, i) => [identity(child, i), child]));
  const matched = new Set();
  next.children.forEach((child, i) => {
    const id = identity(child, i);
    matched.add(id);
    reconcile(before.has(id) ? before.get(id) : null, child, here, ops);
  });
  prev.children.forEach((child, i) => {
    if (!matched.has(identity(child, i))) reconcile(child, null, here, ops);
  });
  return ops;
}

export function printOps(ops) {
  ops.forEach((op) => console.log(`${op.fate}: ${op.path}`));
}

export function countFates(ops) {
  for (const name of FATES) {
    console.log(`${name}: ${ops.filter((op) => op.fate === name).length}`);
  }
  console.log(`komut: ${ops.filter((op) => op.fate !== "korunan").length}`);
}
```

Kuralın bu derste önem taşıyan kısmı `identity` işlevindedir. Anahtarı olan çocuk anahtarıyla, anahtarı olmayan çocuk sırasıyla tanınır. İki ağaçta aynı kimliği taşıyan çocuklar çifttir; çiftin türü ve anahtarı aynıysa özellikleri karşılaştırılır.

Listeyi ağaca çeviren işlev ayrı bir dosyada durur. `renderInbox` ögeleri satır düğümlerine çevirir. İkinci parametresi `true` ise satıra öge numarasını anahtar olarak verir, `false` ise satır anahtarsız kalır.

```javascript
// inbox.mjs — bir gelen kutusunu gorunum agacina cevirir
import { el } from "./fate.mjs";

export const ITEMS = [
  { id: "a", title: "Toplanti" },
  { id: "b", title: "Fatura" },
  { id: "c", title: "Davet" },
];

export const FRESH = { id: "d", title: "Duyuru" };

export function renderInbox(items, useKeys) {
  const rows = items.map((item) =>
    el("Row", { title: item.title }, [], useKeys ? item.id : null));
  return el("Inbox", {}, rows);
}
```

Gelen kutusunda üç öge var ve listenin başına dördüncüsü, `Duyuru`, geliyor. Yeni ögeyi baştan görmek kullanıcının beklediği davranıştır: en yeni ileti en üstte durur.

## Sıraya Göre Eşleme

İlk ölçümde satırlara anahtar verilmez. Eski ağaçta üç satır, yeni ağaçta dört satır vardır. Kural satırları sırayla eşler: eskinin birinci satırı yeninin birinci satırıyla, ikincisi ikinciyle, üçüncüsü üçüncüsüyle çift olur. Yeninin dördüncü satırının eskide karşılığı yoktur.

```javascript
// main.mjs
import { reconcile, printOps } from "./fate.mjs";
import { renderInbox, ITEMS, FRESH } from "./inbox.mjs";

const before = renderInbox(ITEMS, false);
const after = renderInbox([FRESH, ...ITEMS], false);
printOps(reconcile(before, after));
```

```text
korunan: /Inbox
guncellenen: /Inbox/Row
guncellenen: /Inbox/Row
guncellenen: /Inbox/Row
kurulan: /Inbox/Row
```

Üç satır güncellenen, sondaki satır kurulan çıktı. Eklenen öge baştaydı, ama ağaçta yeni olan satır sonda doğdu. Birinci satır önce `Toplanti` gösteriyordu, şimdi `Duyuru` gösteriyor; ikinci satır `Fatura` yerine `Toplanti`, üçüncü satır `Davet` yerine `Fatura` gösteriyor. Hiçbir satır yeni bir ögeyi temsil etmek için kurulmadı: var olan satırlar ögeleri birer basamak kaydırdı ve fazlalık sona eklendi. Üç satırın özelliği değiştiği için üç güncelleme gitti. Kök düğüm `/Inbox` özelliksizdir ve korunan kaldı.

Anahtarsız satırların yolu aynıdır, `/Inbox/Row`, çünkü yol yalnız tür ve anahtardan kurulur. Hangi satırın hangisi olduğunu çıktıdan sıra bildirir. Kuralın içinde ise her biri `#0`, `#1`, `#2` kimliğiyle tanınır.

Bu yalnız fazladan komut demek değildir. Gerçek bir listede satırın kendi durumu da olur: seçili olması, açık olan bir ayrıntı, yazılmış yarım bir yanıt. Sıraya göre eşlemede satır düğümü korunur ama başka bir ögeye geçer. Durum satırda kalır, öge kayar; birinci satırdaki yarım yanıt bir anda `Duyuru` ögesinin yanıtı olur. Kimlik korunumu denen şey burada bozulur: korunan ya da güncellenen düğüm kimliğini sürdürür, ve kimlik yanlış ögeye bağlanmıştır.

## Kararlı Anahtar

İkinci ölçümde aynı ekleme yapılır ama satırlara öge numarası anahtar olarak verilir. Anahtar bir ögeyi sırasından bağımsız tanır: `a` ögesi listenin hangi basamağında olursa olsun `a` kalır. Böyle bir anahtara kararlı anahtar denir.

```javascript
// main.mjs
import { reconcile, printOps } from "./fate.mjs";
import { renderInbox, ITEMS, FRESH } from "./inbox.mjs";

const before = renderInbox(ITEMS, true);
const after = renderInbox([FRESH, ...ITEMS], true);
printOps(reconcile(before, after));
```

```text
korunan: /Inbox
kurulan: /Inbox/Row[d]
korunan: /Inbox/Row[a]
korunan: /Inbox/Row[b]
korunan: /Inbox/Row[c]
```

Şimdi tek bir satır kuruldu: yeni ögenin kendi satırı, `/Inbox/Row[d]`. Eski üç satır anahtarlarıyla eşleşti, özellikleri aynı kaldığı için hepsi korunan çıktı. Yeni satır yalnız ağaçtaki sırasıyla baştadır; kuralın çıktısı da onu başta basıyor. Ayrıca kimlik korunumu bu kez doğrudur: `a` ögesinin satırı, içindeki durumla birlikte, aynı satır olarak sürer.

Anahtarın kararlı olması şarttır. Anahtar olarak listedeki sıra numarası verilirse anahtar sıraya bağlanır ve ilk ölçümdeki durum geri gelir. Anahtar her güncellemede yeniden üretilirse, örneğin rastgele bir sayıdan, her satır her seferinde eski kimliğini yitirir; bütün eski satırlar sökülen, bütün yeni satırlar kurulan olur.

Gerçek çerçevede anahtar, satırı üreten işlevde bir öge numarasından çıkarılır. Sanallaştırılmış listede bu iş anahtar çıkaran işlevin görevidir. Aşağıdaki parça listenin kaynak biçimini gösterir; model bu parçayı çalıştırmaz.

```jsx
// Platform kodu: Inbox
function Inbox({ items }) {
  return (
    <FlatList
      data={items}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => <Row title={item.title} />}
    />
  );
}
```

`keyExtractor` her ögeden bir anahtar çıkarır; model bunu `useKeys` parametresiyle temsil eder. Anahtar çıkaran işlev tanımlı değilse gerçek liste bu yedek davranışı kendi başına seçebilir; model yalnız iki ucu, anahtarsız ve anahtarlı hâli, ölçer.

## Ortadan Silme

Ekleme baştan yapıldığı için kayma en uçtaki durumdu. Silme de aynı sınıftandır ve ortadan silinen bir öge, kuralın iki eşlemesini daha da ayırır. Listeden `b` ögesi, `Fatura`, çıkarılıyor. Kalan iki öge `a` ve `c` yerinde durur.

```javascript
// main.mjs
import { reconcile, countFates } from "./fate.mjs";
import { renderInbox, ITEMS } from "./inbox.mjs";

const remaining = ITEMS.filter((item) => item.id !== "b");
for (const useKeys of [false, true]) {
  console.log(useKeys ? "anahtara gore" : "siraya gore");
  countFates(reconcile(renderInbox(ITEMS, useKeys), renderInbox(remaining, useKeys)));
}
```

```text
siraya gore
korunan: 2
guncellenen: 1
kurulan: 0
sokulen: 1
komut: 2
anahtara gore
korunan: 3
guncellenen: 0
kurulan: 0
sokulen: 1
komut: 1
```

Sıraya göre eşlemede silinen öge `b` olduğu hâlde sökülen satır sondaki satırdır: ikinci satır `Davet` ögesini gösterecek şekilde güncellendi, üçüncü satır eşsiz kaldı ve söküldü. Kararlı anahtarla sökülen satır silinen ögenin kendi satırıdır, `/Inbox/Row[b]`; öteki satırlar korunandır. Komut sayısı ikiye karşı bire düştü.

Bu iki ölçümün ortak dersi şudur: anahtarsız bir listede ağaç, hangi ögenin eklendiğini ya da çıktığını söyleyemez; yalnız sonda bir satırın kaç tane arttığını ya da azaldığını söyler. Anahtar, ağaca ögenin kimliğini ekler. Liste ögeleri sıralanabiliyor, süzülebiliyor ya da kullanıcı tarafından yeniden dizilebiliyorsa bu kimlik hiç isteğe bağlı değildir.

## Pencere ve Sanallaştırma

Kural listenin bütün ögelerini ağaçta görür. Gerçek sanallaştırılmış liste böyle çalışmaz. Binlerce ögelik bir listede yalnız ekranda görünen satırların ve yakın çevresinin yerel görünümü kurulur; bu aralığa pencere denir. Kullanıcı kaydırdıkça pencerenin dışına çıkan satırlar yerel tarafta sökülür ve yeni giren satırlar kurulur. Bu yüzden listenin başarımı, ağaçtaki satır sayısından çok pencerenin genişliğine bağlıdır.

Pencerenin ayarları listenin kaynağında yazılır. Satırın boyu önceden bilinirse liste, satırı kurmadan konumunu hesaplayabilir; pencere genişliği ve ilk açılışta çizilecek öge sayısı da ayardır.

```jsx
// Platform kodu: InboxTuned
function InboxTuned({ items }) {
  return (
    <FlatList
      data={items}
      keyExtractor={(item) => item.id}
      getItemLayout={(_, index) => ({ length: 64, offset: 64 * index, index })}
      windowSize={5}
      initialNumToRender={8}
      renderItem={({ item }) => <Row title={item.title} />}
    />
  );
}
```

Burada yazılan sayılar bir listenin ayarıdır, ölçüm değildir. Hangi değerin işe yaradığını profil aracı gösterir; sonraki ders bu aracı anlatır. Bu derste önemli olan, bu ayarların hiçbirinin kuralın girdisi olmamasıdır. Pencere satırları yerel tarafta kurar ve sökerken ağaç değişmez.

## Sınırlayıcı Ölçüm

İki eşlemeyi yan yana koyalım. Aynı ekleme iki koşumda yapılır, `countFates` her birinin akıbet sayılarını basar. Karşılaştırılan şey komut sayısıdır, yani yerel tarafa giden iş birimi.

```javascript
// main.mjs
import { reconcile, countFates } from "./fate.mjs";
import { renderInbox, ITEMS, FRESH } from "./inbox.mjs";

for (const useKeys of [false, true]) {
  console.log(useKeys ? "anahtara gore" : "siraya gore");
  const before = renderInbox(ITEMS, useKeys);
  const after = renderInbox([FRESH, ...ITEMS], useKeys);
  countFates(reconcile(before, after));
}
```

```text
siraya gore
korunan: 1
guncellenen: 3
kurulan: 1
sokulen: 0
komut: 4
anahtara gore
korunan: 4
guncellenen: 0
kurulan: 1
sokulen: 0
komut: 1
```

Sıraya göre eşleme dört komut, anahtara göre eşleme bir komut verdi. Aynı ekleme için dört komuta karşı bir komut vardır. Üç ögelik bir listede bu fark küçük görünür; fark liste uzadıkça büyür, çünkü başa eklenen her öge, sıraya göre eşlemede kendinden sonraki her satırı bir basamak kaydırır. Kararlı anahtarla komut sayısı listenin uzunluğuna bağlı değildir.

Odak düğüm, sıraya göre eşlemedeki ilk satırdır, `/Inbox/Row`. Aynı yolu taşıyan bütün kayıtları süzelim.

```javascript
// main.mjs
import { reconcile, printOps } from "./fate.mjs";
import { renderInbox, ITEMS, FRESH } from "./inbox.mjs";

const ops = reconcile(renderInbox(ITEMS, false), renderInbox([FRESH, ...ITEMS], false));
printOps(ops.filter((op) => op.path === "/Inbox/Row"));
```

```text
guncellenen: /Inbox/Row
guncellenen: /Inbox/Row
guncellenen: /Inbox/Row
kurulan: /Inbox/Row
```

Birinci kayıt `guncellenen`: ilk satır yerinde kaldı ama başka ögeyi gösteriyor. Dersin akıbeti bu kayıttır.

Kuralın görmediği iki şey var. Kural ağaçtaki bütün ögeleri görür, pencereyi görmez; gerçek listede yerel tarafta bu dört komutun hepsi gerekmeyebilir, çünkü pencerenin dışındaki satırların yerel görünümü yoktur. İkincisi, komutun yerel tarafta tuttuğu iş modelde yoktur: bir satırın metnini değiştirmek ile satırın içindeki görseli yeniden yüklemek aynı sayıda komuttur. İkisini ayırmak profil aracına düşer.

**BU1.** Model, bu akıbetin kuralını kurar; model çerçevenin kendisi değildir. Modelde liste üç ögelidir, pencere yoktur ve her öge ağaçta bir satır düğümüdür. Gerçek bir liste yalnız pencere içindeki ögeler için yerel görünüm kurar, anahtar çıkaran işlev tanımlı değilse kendi yedek davranışını seçer ve satırı belleyerek özelliği değişmeyen satırın yeniden çizimini atlayabilir; model bunların hiçbirini kurmaz. Model anahtarsız çocuğu sırasıyla, anahtarlı çocuğu anahtarıyla eşler ve ölçtüğü iki ucun arasındaki davranışı yorumlamaz.

## Özet

- Bu dersin akıbeti: güncellenen. Sıraya göre eşlemede `/Inbox/Row` düğümü, başa bir öge eklenince yerinde kaldı ama başka ögeyi gösterecek şekilde güncellendi.
- Sıraya göre eşlemede başa eklenen bir öge üç satırı güncelleyip sonda bir satır kurdu; komut sayısı dört oldu.
- Kararlı anahtarla aynı ekleme tek bir `/Inbox/Row[d]` düğümü kurdu, üç eski satır korunan kaldı; komut sayısı bir oldu.
- Sıraya göre eşlemede satırın durumu yanlış ögeye bağlanır; kararlı anahtar kimliği ögeyle birlikte tutar.
- Kural ağaçtaki bütün ögeleri görür, pencereyi ve yerel tarafın işini görmez.

## Sonraki Adım

Bu derste ağaç değişti ve komutlar sayıldı. Bir yeniden çizimin her zaman komut üretmediği de görülmüştür: kararlı anahtarla üç satır, ağaç yeniden üretildiği hâlde komut vermedi. Sıradaki ders, `Kare Hızı ve Profilleme`, ağacı üç kez yeniden üreten bir güncellemenin sıfır komut verebildiğini ölçer.
