---
title: 'Yeniden Oluşturma'
source: 'https://academia.sh/tr/kurslar/compose/yeniden-olusturma'
course: 'Jetpack Compose ile Arayüz'
language: tr
updated: '2026-10-21T05:02:38+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Yeniden oluşturmayı ve girdisi değişmeyen bileşen işlevinin atlanmasını açıklar'
    - 'Değişikliğin yeniden oluşturulan ve atlanan düğümlerini modelde ayırır'
    - 'Okuma sahipte kalırsa değişikliğin her düzeyden aşağı indiğini ölçer'
    - 'Okuma en alttaki düğüme ertelenirse değişikliğin tek derin hareketle ulaştığını gösterir'
---

# Yeniden Oluşturma

Durum değişince platform, değişikliği okuyan bileşen işlevlerini yeniden çalıştırır ve girdisi değişmeyenleri atlar. Ders değişikliğin sahibinden okuyucusuna hangi yolla gittiğini ve okumanın ertelenmesinin yönü nasıl değiştirdiğini ölçer.

Önceki derste olay sahibine döndü ve sahip yeni değeri yazdı. Yazıdan sonra ekranın güncellenmesi gerekir, ama ağacın tamamının yeniden çalışması gerekmez. Platform durum değişince yalnız değişiklikten etkilenebilecek bileşen işlevlerini yeniden çalıştırır. Buna yeniden oluşturma denir. Dersin sorusu yine aynıdır: **bu veri arayüz ağacında hangi yöne gidiyor?** Burada veri, durumun yeni değeridir ya da değiştiğinin haberidir; ve yönü, okumanın nerede yapıldığına bağlıdır.

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

## Yeniden Oluşturma ve Atlama

Platform belgesine göre durum değiştiğinde platform yeniden oluşturma planlar: değişmiş olabilecek bileşen işlevlerini yeniden çalıştırır ve oluşturulan ağacı günceller. Yeniden oluşturma genellikle bir `State<T>` nesnesinin değişmesiyle başlar; platform bu nesneyi okuyan bileşen işlevlerini ve onların çağırdığı, atlanamayan bileşen işlevlerini çalıştırır. Girdisi önceki oluşturmadan değişmemiş uygun bir bileşen işlevi ise tümüyle atlanabilir. Belgeye göre kararlı girdiler `equals` ile, kararsız girdiler güçlü atlama kipinde referans eşitliğiyle karşılaştırılır; girdiler önceki değerlerle eşitse bileşen işlevi atlanır.

Bir yeniden oluşturmanın hangi bileşen işlevlerini kapsadığına yeniden oluşturma kapsamı denir. Değişen durumu okuyan bileşen işlevi kapsamın içindedir. Kapsamın içindeki bir bileşen işlevi çocuğuna değişen bir değer geçirirse çocuk da kapsama girer; değişmeyen girdiyle çağrılan çocuk atlanır.

Sepet ekranında toplamı `CartScreen` hatırlasın ve en alttaki `CartRow` göstersin. Önce değeri sahibinde okuyup parametreyle indiren biçime bakalım.

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

@Composable
fun CartScreen() {
    var total by remember { mutableStateOf(0) }
    Header()
    CartList(total = total)
}

@Composable
fun CartList(total: Int) {
    CartRow(total = total)
}

@Composable
fun CartRow(total: Int) {
    Text("Toplam: $total")
}
```

`CartScreen` değeri kendisi okur ve `CartList`'e geçirir; `CartList` kullanmadan `CartRow`'a iletir. `Header` değişmeyen girdiyle çağrılır. Bu parça platform kodudur ve burada çıktı, düzen, yeniden oluşturma sayısı ya da süre iddia etmiyoruz. Model, platformun bu kuralını kurar; model platformun kendisi değildir.

Model yeniden oluşturmayı üç işleviyle kurar. `scopeChain`, iki düğüm arasındaki yolu verir. `recomposedScopes`, okumanın yapıldığı düğümden değeri kullanan düğüme kadar olan yolu kapsam sayar. `skippedScopes`, kapsamın çocuğu olup kapsamda olmayan düğümleri atlanmış sayar. Ayrıca iki kanalın hareketlerini `valueMoves` ve `deferredMoves` kurar; ikincisini bu dersin ilerisinde kullanacağız.

```kotlin
// Channels.kt — degisikligin sahibinden okuyucuya gittigi iki yol
fun scopeChain(from: String, to: String, tree: Tree): List<String> =
    ((listOf(to) + tree.ancestors(to)).takeWhile { it != from } + from).reversed()

fun recomposedScopes(readAt: String, consumer: String, tree: Tree): List<String> =
    scopeChain(readAt, consumer, tree)

fun skippedScopes(recomposed: List<String>, nodes: List<String>, tree: Tree): List<String> =
    nodes.filter { it !in recomposed && tree.parent(it) in recomposed }

fun valueMoves(owner: String, consumer: String, tree: Tree): List<Move> =
    scopeChain(owner, consumer, tree).zipWithNext()
        .map { (from, to) -> Move("degisim-${to.lowercase()}", from, to, tree) }

fun deferredMoves(owner: String, consumer: String, tree: Tree): List<Move> =
    listOf(Move("degisim-${consumer.lowercase()}", owner, consumer, tree))
```

```kotlin
// Main.kt — okuma sahipte: kapsam yoldaki uc dugumdur, Header atlanir
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App",
        "Header" to "Screen",
        "CartList" to "Screen",
        "CartRow" to "CartList",
    ))
    val nodes = listOf("App", "Screen", "Header", "CartList", "CartRow")
    val recomposed = recomposedScopes("Screen", "CartRow", tree)
    println("yeniden olusturulan: ${recomposed.sorted()}")
    println("atlanan: ${skippedScopes(recomposed, nodes, tree).sorted()}")
}
```

```text
yeniden olusturulan: [CartList, CartRow, Screen]
atlanan: [Header]
```

Okuma `Screen`'de yapıldığı için kapsam `Screen`'den başlar ve değerin indiği yol boyunca `CartList` ve `CartRow`'u içerir. `Header` kapsamın çocuğudur ama girdisi değişmemiştir: atlanır. `App` hiç etkilenmez. Bu modelin atlama için girdilerin kararlı ve eşit olduğunu varsaydığını not edelim; kararlılık sonraki dersin konusudur.

**BI7.** Model kapsamı, okuma düğümünden değeri kullanan düğüme kadar olan yol olarak kurar. Platformda satır içi işlevlerin kapsam sayılmaması, kapsamın bileşen işlevi sınırlarına göre belirlenmesi ve yeniden oluşturmanın kaç kez çalıştığı ölçülmedi.

## Parametre Kanalı: İki Aşağı

Aynı değişikliği hareket olarak ölçelim. Okuma `Screen`'de kaldığında yeni değer `CartList`'e parametreyle geçer, `CartList` onu `CartRow`'a geçirir. Her geçiş ebeveynden doğrudan çocuğadır.

```kotlin
// Main.kt — parametre kanali: her duzeyde bir asagi hareket
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App",
        "Header" to "Screen",
        "CartList" to "Screen",
        "CartRow" to "CartList",
    ))
    val moves = valueMoves("Screen", "CartRow", tree)
    moves.forEach { it.describe() }
    countDirections(moves)
}
```

```text
degisim-cartlist: kimden=Screen | kime=CartList -> asagi
degisim-cartrow: kimden=CartList | kime=CartRow -> asagi
asagi: 2
yukari: 0
derin: 0
dis: 0
```

İki aşağı hareket var. Aradaki `CartList` değeri kullanmıyor, ama yeni değer ona geldiği için yeniden oluşturuluyor. Hareket sayısını ve kapsamı okumanın yeri belirler.

## Okumayı Ertelemek: Tek Derin

Platform belgesi okumanın ertelenmesini bir performans yöntemi olarak anlatır. Durum yukarıda tutuluyorsa ve bir çocukta okunuyorsa, okuma bir geri çağrıya sarılabilir; böylece okuma yalnız gerçekten gerektiğinde ve gerektiği yerde yapılır. Belgeye göre değer yalnız çocuk içinde okunduğunda, değişince en yakın kapsam çocuğun kendisi olur.

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

@Composable
fun CartScreen() {
    var total by remember { mutableStateOf(0) }
    Header()
    CartList(total = { total })
}

@Composable
fun CartList(total: () -> Int) {
    CartRow(total = total)
}

@Composable
fun CartRow(total: () -> Int) {
    Text("Toplam: ${total()}")
}
```

`CartScreen` artık değeri okumaz; değeri okuyan bir geri çağrıyı aşağı verir. Okuma `CartRow` içinde, `total()` çağrıldığında yapılır. Geri çağrının kendisi ilk oluşturmada her düzeyden parametreyle geçer; ama değişen şey geri çağrı değil, durumun değeridir ve bu değişikliğin haberi doğrudan okuyanın kapsamına ulaşır. Modelde değişiklik hareketi sahibinden okuyucuya tek adımda gider.

```kotlin
// Main.kt — okuma ertelendi: kapsam tek dugum, degisiklik tek derin hareket
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App",
        "Header" to "Screen",
        "CartList" to "Screen",
        "CartRow" to "CartList",
    ))
    val nodes = listOf("App", "Screen", "Header", "CartList", "CartRow")
    val recomposed = recomposedScopes("CartRow", "CartRow", tree)
    println("yeniden olusturulan: ${recomposed.sorted()}")
    println("atlanan: ${skippedScopes(recomposed, nodes, tree).sorted()}")
    val moves = deferredMoves("Screen", "CartRow", tree)
    moves.forEach { it.describe() }
    countDirections(moves)
}
```

```text
yeniden olusturulan: [CartRow]
atlanan: []
degisim-cartrow: kimden=Screen | kime=CartRow -> derin
asagi: 0
yukari: 0
derin: 1
dis: 0
```

Kapsam tek düğümdür. `Screen` ile `CartList` yeniden oluşturulmaz, çünkü ikisi de değeri okumaz. Değişiklik `Screen`'den `CartRow`'a tek hareketle, aradaki `CartList`'e uğramadan ulaşır: derin. Dersin yönü budur.

Bu ders 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. Burada neden değil, değişikliğin sahibinden okuyucusuna giden yol ölçülür.

**BI8.** Ertelenmiş kanalda değişikliğin doğrudan okuyanın kapsamına gitmesi platformun gözlem kuralıdır ve modelde böyle kurulmuştur. Gözlemin nasıl çalıştığı, yani okumanın kapsamla nasıl ilişkilendirildiği modelde yoktur.

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

Aynı veri, aynı sahip, aynı okuyucu, iki kanal. Kural kanalı görmez; yalnız iki ucun konumuna bakar. Ama yönün farkı kanaldan gelir ve kapsamın boyutu da.

```kotlin
// Main.kt — ayni veri, iki kanal, iki yon
fun main() {
    val tree = Tree(mapOf(
        "Screen" to "App",
        "Header" to "Screen",
        "CartList" to "Screen",
        "CartRow" to "CartList",
    ))
    val byValue = valueMoves("Screen", "CartRow", tree)
    val deferred = deferredMoves("Screen", "CartRow", tree)
    println("parametre: kapsam=${recomposedScopes("Screen", "CartRow", tree).size} yonler=${byValue.map { it.direction }}")
    println("ertelenmis: kapsam=${recomposedScopes("CartRow", "CartRow", tree).size} yonler=${deferred.map { it.direction }}")
}
```

```text
parametre: kapsam=3 yonler=[asagi, asagi]
ertelenmis: kapsam=1 yonler=[derin]
```

Parametre kanalı iki aşağı hareket ve üç düğümlük kapsam kurar. Ertelenmiş kanal tek derin hareket ve tek düğümlük kapsam kurar. Derin, "daha iyi" demek değildir: ertelenmiş okumada `CartRow`'un içeriği geri çağrıyla okunur ve bunun bedeli kodun okunurluğudur. Ders bu bedeli ölçmez. Ölçtüğü şey, aynı değişikliğin iki kanalda iki farklı yönle ve iki farklı kapsamla `CartRow`'a ulaşmasıdır.

## Özet

- Bu dersin yönü: derin. Okuma en alttaki düğüme ertelenince değişiklik sahibinden okuyucuya tek hareketle ulaşır.
- Okuma sahipte kalınca yeniden oluşturulan düğümler `CartList`, `CartRow` ve `Screen` olur; `Header` atlanır.
- Parametre kanalında değişiklik her düzeyden iner: aşağı 2, derin 0.
- Ertelenmiş kanalda aynı değişiklik tek hareketle gider: aşağı 0, derin 1.
- İki kanal aynı iki ucu bağlar; kapsam 3 düğümden 1 düğüme iner. Kural kanalı görmez, yön farkı kanaldan gelir.

## Sonraki Adım

Bu derste girdisi değişmeyen bileşen işlevlerinin atlandığını varsaydık. Hangi girdinin değişmemiş sayılacağı ayrı bir sorudur ve 06. dersin konusudur. Ondan önce `Yan Etkiler` dersi, yeniden oluşturmanın dışına, ağaç dışında iz bırakan etkilere bakar: hareketin ucu ağaçta olmadığında yön ne olur?
