← Назад к вопросам

Куда функция remember сохраняет состояние?

2.0 Middle🔥 101 комментариев
#UI и вёрстка

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Функция remember в Jetpack Compose

Функция remember — это ключевой механизм в Jetpack Compose для сохранения состояния компонента между перерисовками (recompositions). Она хранит состояние в памяти Composition, специальной структуре данных Compose, которая существует только во время жизни Composition.

Где живёт состояние remember

Состояние, сохранённое в remember, хранится в Composition Tree — внутренней структуре данных Compose, которая отслеживает все компоненты и их состояние.

fun Counter() {
    val count = remember { mutableStateOf(0) }  // Состояние сохраняется здесь
    
    // Эта информация хранится в Composition Tree:
    // Counter Composable
    //   └── remember key
    //       └── mutableStateOf(0)
    
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

Как это работает

Когда компонент вызывается в первый раз:

@Composable
fun MyScreen() {
    val counter = remember { mutableStateOf(0) }
    // 1. remember проверяет: есть ли уже состояние для этого ключа?
    // 2. Нет? Создаёт mutableStateOf(0) и сохраняет в Composition
    // 3. Сохраняет ссылку на это состояние в памяти
}

При перерисовке:

@Composable
fun MyScreen() {
    val counter = remember { mutableStateOf(0) }
    // 1. remember проверяет: есть ли уже состояние для этого ключа?
    // 2. Да! Возвращает сохранённое состояние
    // 3. Лямбда { mutableStateOf(0) } НЕ выполняется снова!
    
    Button(onClick = { counter.value++ }) {
        Text("Count: ${counter.value}")
    }
}
// При клике -> setState -> recomposition -> remember возвращает старое состояние

Граф памяти

JVM Heap
├── Composition Tree
│   ├── MyScreen (Composable)
│   │   ├── remember keys
│   │   │   ├── 0 → MutableState(0)  ← Здесь живёт состояние!
│   │   │   └── 1 → "name"
│   │   └── Child Composables
│   │       └── Button
│   │       └── Text
│   └── ...
├── Recomposition Scope
└── ...

Пример с подробным объяснением

var recompositionCount = 0

@Composable
fun CounterScreen() {
    // remember() привязан к этой позиции в composition tree
    val count = remember { 
        println("Creating mutableStateOf(0) - первый раз")
        mutableStateOf(0) 
    }
    
    recompositionCount++
    println("CounterScreen recomposition #$recompositionCount")
    
    Column {
        Text("Count: ${count.value}")  // Читаем состояние
        Button(onClick = { count.value++ }) {  // Обновляем состояние
            Text("Increment")
        }
    }
}

// Вывод программы:
// Creating mutableStateOf(0) - первый раз  (первая композиция)
// CounterScreen recomposition #1
// (пользователь кликает кнопку)
// CounterScreen recomposition #2          (переключение)
// (лямбда { mutableStateOf(0) } НЕ вызвана!)

Механизм ключей (Keys)

remember использует позицию в composition как ключ для сохранения состояния:

@Composable
fun TodoList(todos: List<String>) {
    Column {
        // ❌ Проблема: ключ меняется при изменении списка
        todos.forEach { todo ->
            val isChecked = remember { mutableStateOf(false) }  // Ключ зависит от позиции
            CheckboxWithLabel(todo, isChecked.value) {}
        }
    }
    // Если удалить первый элемент, состояние переместится на второй элемент!
}

// ✅ Правильно: используйте key() для стабильных ключей
@Composable
fun TodoList(todos: List<String>) {
    Column {
        todos.forEach { todo ->
            key(todo.id) {  // Стабильный ключ
                val isChecked = remember { mutableStateOf(false) }
                CheckboxWithLabel(todo, isChecked.value) {}
            }
        }
    }
}

Иерархия сохранения состояния

@Composable
fun ParentScreen() {
    val parentState = remember { mutableStateOf(0) }
    // Состояние ParentScreen в Composition
    
    ChildScreen(parentState)
}

@Composable
fun ChildScreen(parentState: MutableState<Int>) {
    val childState = remember { mutableStateOf("") }
    // Состояние ChildScreen в Composition (отдельное место)
    
    GrandchildScreen(childState)
}

// Composition Tree:
// ParentScreen
//   ├── remember: mutableStateOf(0)  ← parentState
//   └── ChildScreen
//       ├── remember: mutableStateOf("")  ← childState
//       └── GrandchildScreen
//           └── remember: mutableStateOf(false)  ← grandchildState

Время жизни состояния remember

Состояние помнит (remember) живёт ровно столько же, сколько живит Composition компонента:

@Composable
fun MyScreen() {
    val state = remember { mutableStateOf(0) }
    // Композиция создана
    // Состояние создано и сохранено
}
// При выходе из компонента Composition удаляется
// Состояние тоже удаляется!

@Composable
fun NavigationDemo(screen: String) {
    when(screen) {
        "home" -> {
            val homeState = remember { mutableStateOf(0) }  // Создано
            // homeState живёт пока отображается Home
        }
        "details" -> {
            val detailState = remember { mutableStateOf("") }  // Создано
            // detailState живёт пока отображается Details
            // homeState УДАЛЕНО!
        }
    }
}

remember vs rememberSaveable

@Composable
fun FormScreen() {
    // ❌ remember: состояние теряется при rotation/process kill
    val name = remember { mutableStateOf("") }
    
    // ✅ rememberSaveable: состояние сохраняется в Bundle
    val email = rememberSaveable { mutableStateOf("") }
    // При rotation email восстанавливается, name - нет
    
    Column {
        TextField(value = name.value, onValueChange = { name.value = it })
        TextField(value = email.value, onValueChange = { email.value = it })
    }
}

Под капотом: как Compose управляет состоянием

// Упрощённо, как работает remember
fun <T> remember(calculation: () -> T): T {
    val composition = currentComposition  // Текущая Composition
    val key = currentComposableFunction  // Текущая функция + позиция
    
    // Проверяем, есть ли уже сохранённое значение
    val stored = composition.getState(key)
    
    if (stored != null) {
        return stored as T  // Возвращаем сохранённое
    }
    
    // Первый раз: создаём новое значение
    val newValue = calculation()  // Выполняем лямбду один раз
    composition.setState(key, newValue)  // Сохраняем
    
    return newValue
}

Best Practices

  1. Используйте remember для локального состояния:
@Composable
fun TextField() {
    val text = remember { mutableStateOf("") }
    // Состояние локально для компонента
}
  1. Используйте rememberSaveable для важных данных:
@Composable
fun LoginForm() {
    val username = rememberSaveable { mutableStateOf("") }
    // Сохранится при rotation
}
  1. Поднимайте состояние вверх если нужно делиться:
@Composable
fun ParentScreen() {
    val sharedState = remember { mutableStateOf(0) }
    Child1Screen(sharedState)
    Child2Screen(sharedState)
}

remember — это основа state management в Compose. Состояние хранится в специальной структуре данных Composition и живёт ровно столько же, сколько жива Composition компонента.