---
title: 'Yerel Depolama'
source: 'https://academia.sh/tr/kurslar/react-native/yerel-depolama'
course: 'React Native'
language: tr
updated: '2026-10-21T05:02:53+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Anahtar–değer deposu ile gömülü veritabanı arasındaki seçimi verinin biçimine göre açıklar'
    - 'Açılışta depodan okunan taslağın görünüm ağacında kurulan olduğunu ölçer'
    - 'Depo boşken açılışın hiçbir düğümü değiştirmediğini gösterir'
    - 'Depoya yazmanın görünüm ağacında komut doğurmadığını ölçer'
---

# Yerel Depolama

Yerel depolama, uygulama kapansa da kalması gereken veriyi tutar: küçük değerler için anahtar–değer deposu, sorgulanan kayıtlar için gömülü veritabanı. Ders açılışta depodan okunan taslağın ağaçta kurulan olduğunu, depoya yazmanın ağacı değiştirmediğini ölçer.

Önceki ders sunucudan gelen veriyi ve bellekteki önbelleği ele aldı. Bellek uygulama kapanınca boşalır. Kullanıcının yazdığı bir taslağın, seçtiği bir ayarın ya da indirilmiş bir listenin uygulama yeniden açıldığında da orada olması isteniyorsa veri yerel depolamaya yazılır. Bu ders depolamanın iki türünü ve verinin depodan ağaca nasıl geldiğini ele alır.

Kursun sorusu değişmez: **bu güncelleme görünüm ağacındaki düğüme ne yapıyor?** Akıbet korunan, güncellenen, kurulan ya da sökülendir; güncellenen, kurulan ve sökülen düğümlerin toplamı komuttur. Akıbeti elle atamayız, kural verir:

```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}`);
}
```

Model, bu akıbetin kuralını kurar; model çerçevenin kendisi değildir. Kural ağaçları karşılaştırır, verinin nerede saklandığını görmez.

## Anahtar–Değer Deposu

Anahtar–değer deposu bir adı bir değere bağlar: `taslak` adı bir metne, `tema` adı bir seçime. Okuma ve yazma tek adımdır ve adı bilen değeri alır. Küçük, bağımsız ve sorgulanması gerekmeyen veri için uygundur: ayarlar, son açılan ekran, yarım kalmış bir metin. Depoya erişim uygulamanın ana akışını bekletmemek için eşzamansızdır: okuma bir söz döndürür.

Eşzamansız okuma ekranın ilk çiziminde değerin henüz elde olmadığı anlamına gelir. Ekran önce değersiz açılır, depo yanıt verince durum değişir ve ekran yeniden çizilir.

```jsx
// Platform kodu: DraftEditor
function DraftEditor() {
  const [draft, setDraft] = useState(null);
  useEffect(() => {
    storage.getItem("draft").then(setDraft);
  }, []);
  return <Editor draft={draft} />;
}
```

Bu parça ölçülmez; model depoyu ve çerçeveyi çalıştırmaz.

**UK1.** Model, depoyu bellekteki bir `Map` olarak kurar ve okuma ile yazmayı bir `Promise` ile döndürür. Uygulamanın kapanıp yeniden açılması modele girmez; model yalnız okuma bitince ağaçta ne olduğunu ölçer.

```javascript
// store.mjs — anahtar-deger deposu: bellek ici Map uzerinde Promise ile okur ve yazar
const data = new Map();

export function setItem(key, value) {
  return Promise.resolve().then(() => {
    data.set(key, value);
  });
}

export function getItem(key) {
  return Promise.resolve().then(() => (data.has(key) ? data.get(key) : null));
}
```

**UK2.** Model, ekranın ilk çiziminde taslağı henüz okunmamış sayar; ağaç ilk olarak taslak olmadan kurulur. Taslak varsa `Draft` düğümü ağaca ekranın altında girer, yoksa hiç girmez. `renderEditor(state)` ağacı bu durumdan kurar.

```javascript
// editor.mjs — taslak ekraninin gorunum agacini durumdan kurar
import { el } from "./fate.mjs";

export function renderEditor(state) {
  const children = [el("Title", { metin: "Taslak" })];
  if (state.taslak !== null) children.push(el("Draft", { metin: state.taslak }));
  return el("Editor", {}, children);
}
```

Uygulama açılıyor. Depoda daha önce kaydedilmiş bir taslak var. Ekran önce taslaksız kurulur, sonra okuma bitince taslak durumu ağaca girer.

```javascript
// main.mjs
import { reconcile, printOps, countFates } from "./fate.mjs";
import { renderEditor } from "./editor.mjs";
import { setItem, getItem } from "./store.mjs";

const opening = renderEditor({ taslak: null });
setItem("taslak", "Merhaba")
  .then(() => getItem("taslak"))
  .then((saved) => {
    const ops = reconcile(opening, renderEditor({ taslak: saved }));
    printOps(ops);
    countFates(ops);
  });
```

```text
korunan: /Editor
korunan: /Editor/Title
kurulan: /Editor/Draft
korunan: 2
guncellenen: 0
kurulan: 1
sokulen: 0
komut: 1
```

`/Editor/Draft` kurulandır. Başlık ve ekranın kendisi korunandır. Komut sayısı 1'dir ve o komut depodan okunan veridir. Depo ağacın dışındadır: veri yalnız durum aracılığıyla ağaca girer ve ağaç yalnız düğüm olarak bilir.

Depo boşsa durum değişmez. Okuma `null` döndürür ve ağaç açılıştaki ağaçla aynıdır.

```javascript
// main.mjs
import { reconcile, countFates } from "./fate.mjs";
import { renderEditor } from "./editor.mjs";
import { getItem } from "./store.mjs";

const opening = renderEditor({ taslak: null });
getItem("yok").then((saved) => {
  console.log("bos depo:");
  countFates(reconcile(opening, renderEditor({ taslak: saved })));
});
```

```text
bos depo:
korunan: 2
guncellenen: 0
kurulan: 0
sokulen: 0
komut: 0
```

Depo boşken okuma bir komut doğurmaz. Okumanın bitmiş olması, ağacın değişmesi için yeterli değildir; ağacı değiştiren, okunan değerin durumu değiştirmesidir.

## Gömülü Veritabanı

Veri tek bir değer değil de birçok kayıtsa ve kayıtlar üzerinde sorgu yapılacaksa anahtar–değer deposu yetmez. Gömülü veritabanı uygulamanın içinde çalışır ve kayıtları tablolarda tutar. Kayıtlar kimliğiyle aranır, bir koşula göre süzülür, sıralanır. Bir taslak listesi, sabitlenmiş taslaklar, tarihe göre sıralı kayıtlar bunun örnekleridir.

**UK3.** Model veritabanını, kimlikli satırlardan oluşan sabit bir tablo olarak kurar ve sorguyu tek bir süzgeçle sınırlar: `query(onlyPinned)` tüm satırları ya da yalnız sabitlenmişleri döndürür.

```javascript
// db.mjs — gomulu veritabani: bellek ici satirlari Promise ile sorgular
const ROWS = [
  { id: "t1", baslik: "Giris", sabit: true },
  { id: "t2", baslik: "Sonuc", sabit: false },
  { id: "t3", baslik: "Ekler", sabit: true },
];

export function query(onlyPinned) {
  return Promise.resolve(ROWS.filter((row) => !onlyPinned || row.sabit));
}
```

Sorgu sonucu ekrana bir liste olarak girer. Satırlar kimlikle anahtarlanır; böylece bir süzgeç değişince hangi satırın kaldığı kimlikten bilinir.

```javascript
// main.mjs
import { el, reconcile, printOps, countFates } from "./fate.mjs";
import { query } from "./db.mjs";

function renderDrafts(rows) {
  return el("List", {}, rows.map((row) =>
    el("Item", { baslik: row.baslik }, [], row.id)));
}

Promise.all([query(false), query(true)]).then(([all, pinned]) => {
  const ops = reconcile(renderDrafts(all), renderDrafts(pinned));
  printOps(ops);
  countFates(ops);
});
```

```text
korunan: /List
korunan: /List/Item[t1]
korunan: /List/Item[t3]
sokulen: /List/Item[t2]
korunan: 3
guncellenen: 0
kurulan: 0
sokulen: 1
komut: 1
```

Süzgeç sabitlenmemiş satırı çıkardı. `Item[t2]` sökülendir, kalan iki satır ve liste korunandır. Satırlar kimlikle anahtarlandığı için `t3` satırı sıra kaydığı hâlde başka bir satır sayılmadı. Bu, deponun türünden bağımsız bir sonuçtur: gömülü veritabanı ağaca yalnız satırları verir, kuralın gördüğü satırdır.

## Kaynak Parçayı Satır Satır Okumak

`DraftEditor` parçası, deponun ağaca nasıl bağlandığını üç satırda gösterir. İlk satır durumu `null` ile başlatır: ekran ilk çizildiğinde taslak henüz yoktur. Bu, yukarıdaki modelin ilk varsayımının çerçevedeki karşılığıdır. İkinci parça, açılışta bir kez koşan yan etkidir: depodan okunan söz bitince sonuç durum olarak yazılır. Üçüncü parça, çizimdir: durum ne ise ekran onu gösterir.

Parçada depodan ağaca giden tek yol durumdur. Depo ağaca doğrudan bir düğüm eklemez; okuma biter, durum değişir ve ekran o durumdan yeniden kurulur. Modelin `renderEditor(state)` işlevi bu son adımın karşılığıdır. Aralarındaki fark yalnız şudur: model okumanın bitişini bir söz zinciriyle sıraya koyar, çerçeve aynı bitişi bir yan etkiyle yakalar. Ağaçta görünen akıbet iki yolda da aynıdır, çünkü kural ağaçları karşılaştırır ve oraya hangi yolla varıldığına bakmaz.

Bu okumanın bir sonucu da şudur: ilk çizimde kullanıcı kısa bir an taslaksız bir ekran görür. Depo hızlıysa bu an fark edilmez; yavaşsa ekran önce boş açılır ve taslak sonradan belirir. Bunun bir ağaç karşılığı vardır, `Draft` düğümü ilk kurulumda değil, ikinci karşılaştırmada kurulandır ve ölçümümüz tam olarak bu iki adımı karşılaştırır.

## Taslağı Silmek

Okuma ve yazmanın karşıtı silmedir. Kullanıcı taslağı attığında durum yeniden `null` olur ve `Draft` düğümü ağaçtan çıkmalıdır. Bunu da ölçelim.

```javascript
// main.mjs
import { reconcile, printOps, countFates } from "./fate.mjs";
import { renderEditor } from "./editor.mjs";

const ops = reconcile(
  renderEditor({ taslak: "Merhaba" }),
  renderEditor({ taslak: null }),
);
printOps(ops.filter((op) => op.fate !== "korunan"));
countFates(ops);
```

```text
sokulen: /Editor/Draft
korunan: 2
guncellenen: 0
kurulan: 0
sokulen: 1
komut: 1
```

Açılışın tersi gerçekleşti: kurulan düğüm sökülen oldu. Başlık ve ekran korunandır. Silme bir komut doğurdu; ama bu komut ağacın komutudur, depoyu silmek ayrı bir iştir. Taslağı ağaçtan çıkarmak depodaki değeri silmez. Uygulama yeniden açıldığında depodaki eski taslak yeniden okunur ve `Draft` yeniden kurulur. Kullanıcının attığı taslağın geri gelmemesi için hem durumun hem deponun temizlenmesi gerekir; ağaç yalnız ilkini görür.

## Hangi Depo Ne İçin

İki depo türü ağaçta aynı akıbeti verir ama farklı işler için seçilir. Anahtar–değer deposu adı bilinen tek bir değeri taşır ve okuma ile yazma adımları basittir. Sorgu gerekmeyen küçük veri için yeterlidir. Gömülü veritabanı ise veriye bir yapı verir: satırlar, kimlikler, süzgeçler. Verinin kendisi bir koleksiyonsa ve kullanıcı onu süzüp sıralıyorsa veritabanı doğru seçimdir.

Seçim hatası iki yönde olur. Bir koleksiyonu tek bir anahtara serileştirip her değişiklikte baştan yazmak, her küçük değişikliğin bütün veriyi okuyup yazmasına yol açar. Tek bir ayar değerini ise veritabanında tutmak gereksiz bir yapı kurar. Ağaç bu farkı görmez, ama ekranın tepki biçimi bu seçimden etkilenir: veritabanı kimlikli satırlar verdiği için liste ağacı anahtarlanabilir, tek bir serileştirilmiş değerden anahtar çıkarmak ise ayrıca iş ister.

## Yaygın Yanılgılar

- "Depoya yazınca ekran değişir." Yazma ağaçtaki hiçbir düğümü değiştirmez. Ekranı değiştiren, durumun değişmesidir; kayıt ise durumun bir kopyasını ağacın dışına bırakır.
- "Depodan okumak ekranı hemen doldurur." Okuma eşzamansızdır. Ekran önce değersiz kurulur ve okuma bitince ikinci bir karşılaştırmayla güncellenir; bu ara bir an için de olsa görülebilir.
- "Boş depo bir hatadır." Boş depo okumayı `null` ile bitirir ve durum değişmez. Ağaç açılıştaki ağaçla aynı kalır, komut 0'dır.
- "Ağaçtan çıkan veri depodan da çıkmıştır." Sökülen düğüm depodaki değeri silmez. Kullanıcının attığı veri ancak depodan da silinirse yeniden açılışta geri gelmez.
- "Veritabanı her zaman anahtar–değer deposundan iyidir." Yapı bir bedeldir. Tek bir ayar için tablo kurmak ve satır kimliği düşünmek gereksiz iş getirir; seçim verinin biçimine bakar, deponun gücüne değil.
- "Sökülen düğümün yerel görünümü depoda kalır." Kaydırma konumu gibi yerel görünüm ağaçla birlikte gider; depoda kalan yalnız uygulamanın açıkça yazdığı veridir. Kullanıcının dönünce görmesi gereken şey yazılmadıysa kaybolur.
- "Depoyu okumak her açılışta gerekir." Okuma yalnız ekranın ihtiyaç duyduğu veri için gerekir. Ekranın hiç göstermediği bir değeri açılışta okumak boşa iş yapar; ağaç bu işi görmez ama uygulamanın açılış süreci o işi taşır.
- "Kayıt zaman alıyorsa ekran beklemelidir." Yazma eşzamansızdır ve ağaç yazmanın bitmesini beklemez. Kullanıcı yazdığı metni anında görür; kaydın bittiği ayrıca izlenir.
- "Depodaki veri ile ekrandaki veri her zaman aynıdır." İkisi ayrı yerlerde durur ve yazma bitene kadar farklı olabilir. Kullanıcı yazarken ekran ileridedir, depo geridedir; ağaç yalnız ekranın hâlini gösterir.

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

Depoya yazmak ağacı değiştirmez. Kullanıcı taslağı düzenleyince durum değişir, ağaçta `Draft` güncellenir. Taslağın depoya yazılması ise ayrı bir iştir ve yazma bittiğinde ağaçta yeni bir şey görünmez. Aşağıdaki ölçüm iki adımı ayırır: önce kullanıcı yazar, sonra yazı depoya kaydedilir.

```javascript
// main.mjs
import { reconcile, printOps, countFates } from "./fate.mjs";
import { renderEditor } from "./editor.mjs";
import { setItem } from "./store.mjs";

const opened = renderEditor({ taslak: "Merhaba" });
const typed = renderEditor({ taslak: "Merhaba dunya" });
console.log("yazma:");
printOps(reconcile(opened, typed).filter((op) => op.path === "/Editor/Draft"));
setItem("taslak", "Merhaba dunya").then(() => {
  console.log("kayit:");
  countFates(reconcile(typed, renderEditor({ taslak: "Merhaba dunya" })));
});
```

```text
yazma:
guncellenen: /Editor/Draft
kayit:
korunan: 3
guncellenen: 0
kurulan: 0
sokulen: 0
komut: 0
```

Yazma `Draft` düğümünü güncelledi, çünkü metin özelliği değişti. Depoya kaydetme ise hiçbir düğümü değiştirmedi: komut 0'dır ve `Draft` düğümü korunandır. Depo ağacın dışındaki bir yazmadır; kural onu görmez ve görmesi gerekmez. Kayıt başarısız olsa da ağaç aynı kalırdı. Bu yüzden kaydın doğru yapıldığı ağaca bakarak anlaşılmaz; depodan geri okuyarak anlaşılır.

## Özet

- Bu dersin akıbeti: kurulan. Açılışta depodan okunan taslak ağaca girince `/Editor/Draft` düğümü kurulan çıktı.
- Depo boşken okuma bir komut doğurmaz: ağaç, okuma durumu değiştirmedikçe değişmez.
- Gömülü veritabanında sorgu sonucu bir listedir; süzgeç değişince çıkan satır sökülen, kalan satırlar kimlikle eşlendikleri için korunandır.
- Depoya yazmak görünüm ağacında komut doğurmaz; düzenleme `Draft` düğümünü günceller, kayıt ise hiçbir düğümü değiştirmez.
- Kural deponun türünü ve yazmanın başarısını görmez; yalnız ağaçları karşılaştırır.

## Sonraki Adım

Bu derste depoya yazılan veri taslak gibi sıradan bir metindi. Bazı değerler sıradan değildir: oturum belirteci, parola, anahtar. Bunlar düz bir depoda durmamalıdır. Sıradaki ders, `Güvenli Depolama`, gizli değerlerin nerede saklandığını ve oturum kapanınca ağaçta ne olduğunu ölçer.
