Подъём состояния (state hoisting)
Учимся выносить состояние из компонента наверх, чтобы делать переиспользуемые и тестируемые composable.
State hoisting — паттерн, при котором состояние выносится из composable в вызывающий код: компонент получает значение через параметр и сообщает об изменениях через колбэк.
Однонаправленный поток данных
В Compose данные текут вниз, события — вверх. Компонент получает value сверху и не меняет его сам, а сообщает «пользователь хочет изменить» через колбэк onValueChange. Решение, как реагировать, принимает владелец состояния.
@Composable
fun NameField(value: String, onValueChange: (String) -> Unit) {
TextField(value = value, onValueChange = onValueChange)
}Сам NameField не хранит текст. Он только показывает то, что ему дали, и сообщает о вводе. Это делает его управляемым (stateless) и переиспользуемым. Запомните простое правило обозначений: параметр value (или checked, selected) — это «данные вниз», а парный к нему колбэк onValueChange (или onCheckedChange) — «событие вверх». Вы встретите эту пару почти у каждого интерактивного компонента Compose.
Кто хранит состояние
Состояние держит компонент выше по дереву:
@Composable
fun NameScreen() {
var name by remember { mutableStateOf("") }
NameField(value = name, onValueChange = { name = it })
}Так данные текут от NameScreen вниз в NameField (через value), а событие ввода поднимается обратно вверх (через onValueChange). Это и есть однонаправленный поток.
Схема потока
NameScreen (хранит name)
| value = name (данные вниз)
v
NameField (показывает)
^
| onValueChange(it) (событие вверх)
|Как работает под капотом
Подъём состояния не магия — это обычная передача параметров. Выгода в том, что один источник истины (name в NameScreen) управляет всем. Stateless-компонент проще тестировать: ему просто передают значение и проверяют отрисовку. А ещё его можно переиспользовать в разных экранах с разными владельцами состояния.
Частые ошибки
- Хранить состояние внутри низкоуровневого компонента, который хочется переиспользовать — он становится негибким.
- Поднимать состояние «слишком высоко», в самый верх дерева, когда им пользуется лишь один маленький компонент — лишние перерисовки и связность.
- Менять
valueвнутри компонента напрямую вместо вызоваonValueChange— нарушается однонаправленный поток.
Итог
- Данные текут вниз через
value, события — вверх через колбэки. - Stateless-компоненты переиспользуемы и легко тестируются.
- Поднимайте состояние ровно до того уровня, где им реально нужно управлять.