---
title: 'Durum ve Hatırlama'
source: 'https://academia.sh/tr/kurslar/compose/durum-ve-hatirlama'
course: 'Jetpack Compose ile Arayüz'
language: tr
updated: '2026-10-21T05:02:38+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Hatırlanan durumun bir düğüme bağlı olduğunu ve düğüm oluşturmadan çıkınca yitirildiğini gösterir'
    - 'Aynı bileşen işlevinin iki çağrısının ayrı durum tuttuğunu ölçer'
    - 'Durumun sahibinden okuyucusuna giden değerin aşağı gittiğini kuralla belirler'
    - 'Yalnız kendi düğümünde okunan yerel durumun hiçbir harekete girmediğini açıklar'
---

# Durum ve Hatırlama

Bir bileşen işlevi çağrıdan çağrıya hiçbir şey saklamaz; saklanacak değeri remember bir düğüme bağlar. Ders durumun düğüme bağlı oluşunu ve sahibinden okuyucusuna giden değerin yönünü ölçer.

Önceki derste veri sahibinden okuyucusuna parametreyle indi. Parametre bir değerin yanından geçtiği yoldur; değerin kendisi nerede yaşar? Bir bileşen işlevi her çağrıldığında baştan çalışır ve yerel değişkenleri çağrı bitince kaybolur. Bir sayacın değeri, bir metin alanının içeriği ya da bir listenin açık olup olmadığı çağrıdan çağrıya taşınmak zorundadır. Bunu yapan düzenek hatırlamadır: `remember`, bir değeri arayüz ağacındaki bir düğüme bağlar.

Ders iki şey ölçer. Birincisi, durumun düğüme bağlı oluşudur. İkincisi, durumun sahibinden okuyucusuna giden değerin yönüdür: **bu veri arayüz ağacında hangi yöne gidiyor?** Sınıflandırıcı önceki derslerdeki gibi ilk blokta durur.

```kotlin
// Direction.kt — bir verinin arayuz agacinda hangi yone gittigini siniflandirir
class Tree(private val parentOf: Map<String, String>) {
    fun contains(node: String): Boolean = node in parentOf.keys || node in parentOf.values
    fun parent(node: String): String? = parentOf[node]
    fun ancestors(node: String): List<String> {
        val chain = mutableListOf<String>()
        var current = parentOf[node]
        while (current != null) {
            chain.add(current)
            current = parentOf[current]
        }
        return chain
    }
}

class Move(val label: String, val from: String, val to: String, tree: Tree) {
    val direction: String = when {
        !tree.contains(from) || !tree.contains(to) -> "dis"
        to in tree.ancestors(from) -> "yukari"
        tree.parent(to) == from -> "asagi"
        from in tree.ancestors(to) -> "derin"
        else -> error("akrabasiz: $from ve $to")
    }

    fun describe() {
        println("$label: kimden=$from | kime=$to -> $direction")
    }
}

fun countDirections(moves: List<Move>) {
    for (name in listOf("asagi", "yukari", "derin", "dis")) {
        println("$name: ${moves.count { it.direction == name }}")
    }
}
```

## Durum Bir Düğüme Bağlanır

Platformda durum, değeri gözlemlenebilir kılan `mutableStateOf` ile kurulur. `remember` bu durum nesnesini oluşturmanın sonucunu arayüz ağacına yazar ve bileşen işlevi yeniden oluşturulduğunda aynı nesneyi geri verir. Çağıran bileşen işlevi ağaçtan kalkarsa hatırlanan nesne de unutulur. Platform belgesi bunu şöyle söyler: `remember`, değeri oluşturma sırasında saklar ve yeniden oluşturmada saklanan değeri döndürür; `remember`'ı çağıran bileşen işlevi ağaçtan kaldırıldığında nesneyi unutur.

```kotlin
// Platform kodu: Counter
import androidx.compose.runtime.*

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Sayı: $count")
    }
}
```

`Counter`, `count` değerini kendi düğümüne bağlar. Düğmeye her dokunuş değeri yazar, yazma da değeri okuyan bileşen işlevlerinin yeniden oluşturulmasını ister. Bu parça platform kodudur ve burada çıktı, düzen ya da süre iddia etmiyoruz. Ölçümü düz Kotlin değerleriyle kurulan bir model yapar. Model, platformun bu kuralını kurar; model platformun kendisi değildir.

Model hatırlamayı düğüm adıyla anahtarlanan bir eşlem olarak taşır. Platform durumu ağaçtaki konuma bağlar; modelde konumun yerini düğümün adı tutar.

```kotlin
// Slots.kt — durumu dugume baglayan hatirlama modeli
class Slots {
    private val values = mutableMapOf<String, Int>()
    fun remember(node: String, initial: Int): Int = values.getOrPut(node) { initial }
    fun write(node: String, value: Int) { values[node] = value }
    fun leave(node: String) { values.remove(node) }
    fun holds(node: String): Boolean = node in values
}
```

`remember` ilk çağrıda başlangıç değerini yazar, sonraki çağrılarda yazılı değeri geri verir. `write` durumu değiştirir. `leave` düğümün ağaçtan çıkışını kurar: düğümün satırı silinir.

```kotlin
// Main.kt — durum dugume baglidir, dugum cikinca yitirilir
fun main() {
    val slots = Slots()
    println("ilk olusturma: ${slots.remember("Screen", 0)}")
    slots.write("Screen", 3)
    println("yeniden olusturma: ${slots.remember("Screen", 0)}")
    slots.leave("Screen")
    println("agactan cikis: sakli=${slots.holds("Screen")}")
    println("yeniden giris: ${slots.remember("Screen", 0)}")
}
```

```text
ilk olusturma: 0
yeniden olusturma: 3
agactan cikis: sakli=false
yeniden giris: 0
```

Yeniden oluşturmada başlangıç değeri 0 yeniden verildi, ama geri gelen 3 oldu: hatırlanan değer başlangıç değerini yendi. Düğüm ağaçtan çıkıp yeniden girince durum sıfırdan başladı. Bu yüzden bir bileşen işlevinin ekrandan kalkıp geri gelmesi, yazılan değerin kaybolması demektir.

**BI3.** Model hatırlamayı yalnız tam sayı değerlerle ve düğüm adıyla kurar. Platformda anahtarın çağrının ağaçtaki konumu olması, `remember`'a verilen anahtarların önbelleği boşaltması ve gözlemlenebilir durumun yazmada okuyucuları işaretlemesi modelde yoktur.

## Her Çağrının Kendi Durumu Vardır

Aynı bileşen işlevi ağaçta iki yerde çağrılırsa iki düğüm oluşur ve her düğüm kendi durumunu tutar. Sepet satırları bunun örneğidir: aynı `CartRow` işlevi iki kez çağrılır, ama birinin adedini artırmak ötekini değiştirmez.

```kotlin
// Main.kt — ayni islev, iki cagri, iki ayri durum
fun main() {
    val slots = Slots()
    val rows = listOf("CartRow1", "CartRow2")
    rows.forEach { slots.remember(it, 1) }
    slots.write("CartRow1", 5)
    rows.forEach { println("$it: ${slots.remember(it, 1)}") }
}
```

```text
CartRow1: 5
CartRow2: 1
```

Durum işlevin değil, işlevin ağaçtaki çağrısının malıdır. Bu ayrım sonraki derste önem kazanır: bir değeri iki kardeş paylaşacaksa o değer kardeşlerden birinin düğümünde duramaz.

## Hatırlanan Değer Okuyucusuna İner

Durumu hatırlayan düğüm onun sahibidir. Çocuklar değeri sahibinden parametreyle alır. Model, bir düğümün okuduğu durumun sahibini düğümün kendisinden başlayıp atalarına doğru yürüyerek bulur: değeri hatırlayan ilk düğüm sahiptir.

```kotlin
// Holder.kt — bir dugumun okudugu durumu hangi dugumun hatirladigini bulur
fun holderOf(node: String, slots: Slots, tree: Tree): String? =
    (listOf(node) + tree.ancestors(node)).firstOrNull { slots.holds(it) }
```

Sepet ekranında adedi `Screen` hatırlasın; `Header` ve `CartList` onu okusun. Hareketlerin bir ucu `holderOf` dönüşünden gelir, yön elle yazılmaz.

```kotlin
// Main.kt — Screen hatirlar, cocuklar okur: iki asagi hareket
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App",
        "Header" to "Screen",
        "CartList" to "Screen",
        "CartRow" to "CartList",
    ))
    val slots = Slots()
    slots.remember("Screen", 2)
    val readers = listOf("Header", "CartList")
    val moves = readers.map { reader ->
        Move("adet-${reader.lowercase()}", holderOf(reader, slots, tree)!!, reader, tree)
    }
    moves.forEach { it.describe() }
    countDirections(moves)
}
```

```text
adet-header: kimden=Screen | kime=Header -> asagi
adet-cartlist: kimden=Screen | kime=CartList -> asagi
asagi: 2
yukari: 0
derin: 0
dis: 0
```

Sahibi `Screen` çıktı ve değer onun iki doğrudan çocuğuna iner: iki aşağı hareket. Bu dersin yönü budur. `CartRow` da adedi okusaydı, değeri `CartList` üzerinden parametreyle alırdı; bu, geçen dersin ölçtüğü gibi iki ayrı aşağı hareket olurdu.

Bu ayrım M30/K03'ten farklıdır. O kursun "bu yeniden hesaplamayı ne tetikledi" sorusu, bir görünümün neden yeniden hesaplandığını okuyanın gözünden sınıflar: yerel, ebeveyn, gözlem, ortam. Buradaki durum yazması da okuyucuların yeniden oluşturulmasını ister, ama ders bunu ölçü yapmaz. Ölçtüğümüz tetik değil, değerin sahibinden okuyucusuna giden yoldur.

## Kaydedilebilir Durum

Platform belgesine göre `remember` ile tutulan durum yeniden oluşturmalar boyunca saklanır, ama yapılandırma değişikliklerinde korunmaz; bunun için `rememberSaveable` kullanılır. `rememberSaveable`, durumu kayıtlı örnek durumu düzeneğiyle activity ya da süreç yeniden yaratıldığında da korur, örneğin ekran döndürüldüğünde. Kullanıcı activity'yi tümüyle kapatırsa korumaz.

```kotlin
// Platform kodu: SavedCounter
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable

@Composable
fun SavedCounter() {
    var count by rememberSaveable { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Sayı: $count")
    }
}
```

Yazım biçimi `remember` ile aynıdır; fark değerin nerede saklandığıdır. `Bundle`'a yazılabilen değerler kendiliğinden kaydedilir, öteki tipler için bir kaydedici verilir. Modelimiz sistemin yeniden yarattığı ortamı kurmadığı için bu davranış burada ölçülmez; parça KAYNAK kalır.

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

Kural bir hareketi sınıflar, hareketsizliği sınıflamaz. Durum yalnız kendi düğümünde okunuyorsa kaynağı ile hedefi aynı olur ve böyle bir hareket kurulmaz; `Move` onu sınıflamaya çalışmaz, çünkü ortada hareket yoktur. Aynı `Counter` düğümünü iki biçimde kuralım: durumu kendisi hatırladığında ve durum bir üst düğümde durduğunda.

```kotlin
// Main.kt — yerel durum hareket degildir
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App",
        "Counter" to "Screen",
        "Header" to "Screen",
    ))
    for ((holder, label) in listOf("Counter" to "yerel", "Screen" to "ortak")) {
        val slots = Slots()
        slots.remember(holder, 0)
        val moves = listOf("Counter")
            .filter { holderOf(it, slots, tree) != it }
            .map { Move("adet-okuma", holderOf(it, slots, tree)!!, it, tree) }
        println("durum $label:")
        moves.forEach { it.describe() }
        countDirections(moves)
    }
}
```

```text
durum yerel:
asagi: 0
yukari: 0
derin: 0
dis: 0
durum ortak:
adet-okuma: kimden=Screen | kime=Counter -> asagi
asagi: 1
yukari: 0
derin: 0
dis: 0
```

Yerel durumda hareket listesi boş kalır ve dört yönün hepsi sıfır basılır. Durum bir üst düğüme çıkınca aynı okuma bir aşağı harekete döner. Kural durumun yerelken "yok" olduğunu söylemez; yalnız listeye girmeyen bir şeyi saymaz. Bu fark sonraki derste önem kazanır: durumu yukarı kaldırmak, yerel bir değeri hareket üretir hâle getirmektir.

**BI4.** Model okuma hareketini yalnız durumun sahibinden okuyucusuna kurar. Okuyucunun durumu yazması, yani ters yöndeki olay bu derste ölçülmedi; bir sonraki dersin konusudur.

## Özet

- Bu dersin yönü: aşağı. Hatırlanan durumun sahibinden okuyucusuna giden değer, doğrudan çocuğa aşağı iner.
- `remember` değeri bir düğüme bağlar: yeniden oluşturmada saklanan 3 geri geldi, düğüm ağaçtan çıkıp yeniden girince değer 0'a döndü.
- Aynı işlevin iki çağrısı ayrı durum tutar: `CartRow1` için 5, `CartRow2` için 1.
- `Screen`'in hatırladığı adedi iki doğrudan çocuk okuyunca ölçüm aşağı 2 verdi.
- Yalnız kendi düğümünde okunan yerel durum hareket üretmez: dört yönün hepsi 0; durum bir üst düğümde durunca aşağı 1.

## Sonraki Adım

Bu derste değer sahibinden okuyucusuna indi. Okuyucu değeri değiştirmek istediğinde ne olur? Sıradaki ders, `Durum Kaldırma`, durumu okuyucuların ortak atasına taşır ve değiştirme isteğinin sahibine hangi yönde döndüğünü ölçer.
