Куда функция remember сохраняет состояние?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Функция 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
- Используйте remember для локального состояния:
@Composable
fun TextField() {
val text = remember { mutableStateOf("") }
// Состояние локально для компонента
}
- Используйте rememberSaveable для важных данных:
@Composable
fun LoginForm() {
val username = rememberSaveable { mutableStateOf("") }
// Сохранится при rotation
}
- Поднимайте состояние вверх если нужно делиться:
@Composable
fun ParentScreen() {
val sharedState = remember { mutableStateOf(0) }
Child1Screen(sharedState)
Child2Screen(sharedState)
}
remember — это основа state management в Compose. Состояние хранится в специальной структуре данных Composition и живёт ровно столько же, сколько жива Composition компонента.