Не совершайте эту ошибку состояния в 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. если вам понравилась статья, не забудьте похлопать, и если вы являетесь поклонником или хотите поддержать мой контент, рассмотрите возможность оставить чаевые/кофе/пожертвования💰здесь