Состояние и рекомпозиция
Состояние в Compose — это данные, которые могут меняться со временем; при их изменении Compose автоматически перезапускает зависимые composable, и этот процесс называется рекомпозицией.
Суть: связка remember и mutableStateOf делает интерфейс живым — изменение состояния автоматически перерисовывает только те части UI, что от него зависят.
Статичный экран бесполезен — приложение должно реагировать на действия. В Compose реакция строится на состоянии. Чтобы Compose следил за изменением значения, его оборачивают в mutableStateOf, а чтобы значение пережило рекомпозицию — в remember. Когда состояние меняется, Compose заново вызывает зависимые composable и обновляет картинку.
import androidx.compose.runtime.*
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Нажато: $count раз")
}
}Здесь count — состояние. По клику оно увеличивается, Compose замечает изменение и перерисовывает кнопку с новым текстом. Никакого ручного findViewById и setText — обновление декларативно.
Что такое рекомпозиция
Рекомпозиция — это повторный вызов composable-функций при изменении состояния, от которого они зависят. Compose умён: он перерисовывает не весь экран, а только те функции, которые читают изменившееся состояние. Это ключ к производительности декларативного UI.
Цикл состояния и UI
Состояние (count = 0)
|
v
Composable читает count --> рисует "Нажато: 0 раз"
^ |
| count++ (клик) |
+-----------------------------------+
|
v
РЕКОМПОЗИЦИЯ: перерисовка с count = 1Зачем нужен remember
Без remember значение пересоздавалось бы при каждой рекомпозиции и счётчик всегда сбрасывался бы в ноль. remember сохраняет объект между рекомпозициями. Важный нюанс: remember не переживает поворот экрана. Чтобы значение сохранилось и при пересоздании Activity, используют rememberSaveable.
// переживёт поворот экрана
var name by rememberSaveable { mutableStateOf("") }Как работает под капотом
Compose ведёт внутреннюю таблицу слотов — структуру, в которой запоминает результаты remember для каждой позиции в дереве. mutableStateOf создаёт наблюдаемое состояние: каждое чтение этого состояния внутри composable «подписывает» функцию на изменения. Когда значение меняется, Compose помечает подписанные функции как требующие рекомпозиции и перезапускает их в следующем кадре. Синтаксис by — это делегат, позволяющий читать и писать состояние как обычную переменную.
Частые ошибки
Забыть remember. Без него состояние сбрасывается на каждой рекомпозиции — счётчик не работает.
Менять обычную переменную вместо состояния. Изменение не-наблюдаемой переменной не вызывает рекомпозицию, и UI не обновляется.
Долгие операции прямо в теле composable. Тело может вызываться часто (на каждой рекомпозиции); тяжёлую работу выносят в side-effect или ViewModel.
Best practices
- Изменяемые данные UI оборачивайте в
remember { mutableStateOf(...) }. - Для переживания поворота используйте
rememberSaveable. - Не выполняйте побочные эффекты прямо в теле composable.
- Держите composable чистыми: один и тот же ввод — один и тот же UI.
Цикл «состояние меняется — UI пересобирается» удобно смоделировать на Python как простой счётчик с перерисовкой. Запустите врезку.
# Аналог состояния и рекомпозиции из Compose
def render(count):
# как тело composable: UI зависит только от состояния
return 'Кнопка [ Нажато: ' + str(count) + ' раз ]'
count = 0
print(render(count)) # начальное состояние
for _ in range(3):
count += 1 # клик меняет состояние
print(render(count)) # рекомпозиция: новая отрисовкаПопробуй сам ▶ — добавьте уменьшение счётчика или сброс в ноль и вызывайте render после каждого изменения. В Compose render вызывается автоматически при изменении состояния.
Закрепим главное
Зафиксируйте причинно-следственную связь: меняется наблюдаемое состояние — происходит рекомпозиция зависимых функций. Всё остальное в Compose вращается вокруг этого. Если UI не обновляется, почти всегда причина одна: вы изменили обычную переменную, а не состояние, и Compose просто не узнал об изменении. Привычка спрашивать «это состояние наблюдаемое?» снимает большинство ранних вопросов «почему экран не реагирует».
Второй ориентир — чистота composable. Тело функции может вызываться часто и в непредсказуемые моменты, поэтому в нём не место побочным эффектам и тяжёлой работе: запросам, записи в файл, изменению глобальных переменных. Composable должен быть функцией от состояния — те же входные данные дают тот же UI. Эффекты выносят в специальные конструкции и в ViewModel. Эта дисциплина делает перерисовку безопасной и предсказуемой, что мы и используем, поднимая состояние в следующих уроках.
Итог: состояние через mutableStateOf и remember делает Compose-интерфейс живым, а рекомпозиция эффективно обновляет только нужные части. Дальше мы разберём, как правильно организовать состояние между composable.