remember и mutableStateOf
Разбираем, как Compose запоминает значения между перерисовками и как сделать состояние «наблюдаемым».
State в Compose — это наблюдаемое значение: при его изменении автоматически запускается перерисовка зависящих от него composable.
Зачем нужен remember
Composable-функция может выполняться много раз. Если объявить переменную обычным способом, при каждой перерисовке она будет создаваться заново и обнуляться. remember говорит Compose: «сохрани это значение между вызовами функции».
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text(text = "Нажато: $count")
}
}Здесь работают две вещи вместе, и важно их не путать. mutableStateOf(0) создаёт наблюдаемое состояние со значением 0 — это объект, за изменениями которого Compose следит. remember { ... } сохраняет этот объект между перерисовками, чтобы счётчик не сбрасывался. Одно без другого не работает: mutableStateOf без remember будет каждый раз создавать новый «нулевой» объект, а remember без mutableStateOf сохранит обычное число, изменения которого Compose не заметит.
Зачем тут by
Ключевое слово by — это делегат свойств Kotlin. Он позволяет писать count вместо count.value. Без делегата код выглядел бы так:
val count = remember { mutableStateOf(0) }
Button(onClick = { count.value++ }) {
Text(text = "Нажато: ${count.value}")
}Что произойдёт при клике
Нажатие меняет count. Compose замечает, что наблюдаемое состояние изменилось, и перевыполняет те composable, которые читали это значение — в нашем случае Text внутри Button. Новый текст отрисовывается, остальное не трогается.
Как работает под капотом
Объект из mutableStateOf хранит значение и список «подписчиков» — мест, где оно читалось во время отрисовки. Когда вы присваиваете новое значение, Compose помечает эти места как устаревшие и ставит их в очередь на recomposition. remember же хранит объект в специальной памяти композиции, привязанной к месту вызова в дереве: при следующем проходе функция получает тот же самый объект состояния, а не новый.
Частые ошибки
- Объявить
var count = 0безrememberиmutableStateOf— значение будет сбрасываться, а UI не обновится. - Использовать
remember { 0 }безmutableStateOf— значение сохранится, но Compose не будет следить за его изменениями, перерисовки не случится. - Читать состояние вне composable и удивляться, что UI не реагирует.
Итог
mutableStateOfсоздаёт наблюдаемое значение, изменение которого вызывает перерисовку.rememberсохраняет объект между recomposition, чтобы он не пересоздавался.- Делегат
byпозволяет работать со значением напрямую, без.value.