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.
Проверьте себя
1. Что делает remember в Compose?
AДелает значение наблюдаемым
BСохраняет значение между recomposition, чтобы оно не пересоздавалось
CЗапускает корутину
DКеширует данные на диск
2. Зачем нужен mutableStateOf?
AЧтобы сохранить значение между запусками приложения
BЧтобы сделать значение наблюдаемым и запускать перерисовку при его изменении
CЧтобы превратить функцию в composable
DЧтобы ускорить компиляцию
3. Что будет, если объявить var count = 0 без remember и mutableStateOf?
AСчётчик будет работать корректно
BЗначение будет сбрасываться при перерисовке, а UI не обновится
CПроизойдёт ошибка компиляции
DCounter станет глобальной переменной