Не совершайте эту ошибку состояния в Jetpack/Jetbrain Compose — деструктуризация состояния против делегатов состояния.
Показан на: DroidCon | ПроАндроидДев | Либхант | АндроидЕженедельник | Commonware | АндроидСтэк | КотлинТренд
Существует два способа создания изменяемого состояния в Jetpack/Jetbrain Compose. Первый и мой любимый способ — использовать Destructuring
, а другой способ — использовать Delegate
.
Изменяемое состояние с деструктурированием 🔨
Если вы не знакомы с концепцией деструктурирования в Kotlin, узнайте все о них здесь:
Если вы посмотрите на Подпись MutableState:
Он имеет операторы component1
и component2
, что означает, что его можно уничтожить следующим образом:
val(count, setCount) = mutableStateOf(0)
Где count
получает свое значение от component1
, а setCount
— это лямбда, полученная от component2
.
Изменяемое состояние с делегатами 🍇
Узнайте делегата на официальном канале:
https://kotlinlang.org/docs/delegated-properties.html
Если вы посмотрите на документацию State и Mutable State:
getValue(...)
определяется поверх State
, а setValue(...)
определяется поверх MutableState
, поэтому мы можем использовать концепцию делегата поверх этого примера:
Мы управляем чтением и записью состояния, определяя ссылочный тип переменной val
или var
.
По моему личному мнению, использование способа Destructuring
кажется мне более удобным для чтения и сопровождения, поскольку я знаю, где произошла операция set, отслеживая setCount()
, и где происходит операция get, отслеживая count
. В то время как отслеживать состояние, созданное из делегата, вам будет трудно, но по моему опыту я чувствую, что работа с var
в конечном итоге легко используется разработчиками.
Кроме того, в сценариях, где я пересылаю состояние составным дочерним элементам, мне не нужно создавать лямбда-выражения на месте, я могу напрямую передать ссылку, например:
Ну, до сих пор я был поклонником деструктурирования изменяемых состояний, но недавно я столкнулся со сценарием, который вызвал у меня беспокойство по поводу этого подхода.
Рассмотрим следующий код: я увеличиваю состояние на 1 при нажатии кнопки и отображаю его в пользовательском интерфейсе:
Выход :
Я хочу обновить фрагмент, чтобы увеличить его на 2 вместо 1, см. следующие изменения, которые я сделал:
Выход :
🤯 WTF — не получилось!
Разбираемся 🤔 почему?
когда вы нажимаете на кнопку, в фоновом режиме происходит следующее:
но почему??? setCount()
должно быть в изменяемом состоянии, верно??
Что сломалось?? это Compose или MutableState?? :
Ну ни один не сломался. Я немного углубился в то, что произошло, и понял, что:
- Compose внутренне отслеживает все операции чтения и записи в состояние, нам не нужно понимать внутреннюю работу для этого обсуждения, но вам просто нужно знать, что compose работает в системе моментальных снимков, все записи, которые вы делаете в изменяемом состоянии, влияют на этот снимок.
- Если между предыдущим и новым моментальным снимком возникает конфликт, то для его разрешения будет выполнено какое-то действие. С точки зрения Compose Framework это приведет к триггеру для повторной композиции.
- Поэтому, когда значение изменяемого состояния обновляется, оно проверяет политику моментальных снимков и проверяет, есть ли у моментального снимка какой-либо конфликт,
- если ответ на этот вопрос положительный, то изменяемое состояние сообщит компоновщику о необходимости перекомпоновать и сохранить последнее значение, но вместо того, чтобы сделать последнее значение значением немедленного состояния, оно установит его в качестве значения-кандидата для следующего цикла перекомпоновки для чтения от.
- Таким образом, даже если вы выполнили несколько обновлений состояния, последнее установленное вами значение будет рассматриваться как последний кандидат на значение состояния.
- Когда произойдет рекомпозиция, он считывает последнее значение из изменяемого состояния и применяет изменения.
🤷♂️Хорошо… значит ли это, что нам нужно дождаться, пока значение-кандидат станет реальным значением, то есть до перекомпоновки для обновления состояния? т. е. выполнение нескольких операций набора состояний подряд неправильно при составлении???
Ну, в большинстве случаев мы можем избежать этой ситуации, если мы создадим launchEffect
и поместим в него нашу логику изменения состояния или рефакторим нашу логику, чтобы создать только одно установленное состояние.
но в идеале это не то поведение, которое мы ожидаем от обновления состояния.
Давайте посмотрим на тот же код, используя шаблон делегата:
Выход :
этот фрагмент выполнит операцию правильно! 🤯. Почему это случилось? Разве это правило не применяется к поведению?
Ну нет! Создание состояния с деструктурированием ≠ Создание состояния с делегатом
Внутри произошло то же самое, но когда вы используете метод делегирования состояния, всякий раз, когда вы читаете значение из состояния, оно делегирует операцию перехода к MutableState.value
, которая возвращает значение-кандидат вместо реального значения (если идентификаторы моментальных снимков изменились) .
Но когда вы работаете с состоянием деструктуризации, component1
вызывал MutableState.value
только один раз во время деструктуризации и устанавливал его в переменную count
, т.е. геттерную часть. если вы снова прочитаете его из геттера, он не делегирует задачу проверке MutableState.value
и не получит значение-кандидат, он просто вернет последнее вычисленное значение при деструктуризации.
это означает, что при использовании Destructuring вы написали код, похожий на:
Таким образом, вам нужно дождаться рекомпозиции, чтобы повторно инициализировать значение изменяемого состояния.
Таким образом, на самом деле никакое поведение Compose или MutableState не нарушается, это просто способ деструктурирования и делегирования в языке.
Вывод 💆🏻♀️
Хотя я по-прежнему считаю, что состояние Destructuring
лучше, чем состояние Delegating
, но для работы с непротиворечивым значением состояния лучше всего использовать путь делегирования.
Спасибо, что дочитали до этого места. Если вам понравилась моя статья, ознакомьтесь с моей статьей и контентом с https://chetangupta.net/
Если вы хотите, чтобы я написал статью или обучил команды Android для вашего бизнеса, свяжитесь с нами, нажав👉 здесь.
До скорого! Удачного взлома 👨🏻💻
P.S. если вам понравилась статья, не забудьте похлопать, и если вы являетесь поклонником или хотите поддержать мой контент, рассмотрите возможность оставить чаевые/кофе/пожертвования💰здесь