Why bother `remember` state in Jetpack Compose?

Po C.
4 min readNov 22, 2021

--

Jetpack Compose, the new modern UI toolkit for Android (and other platforms), is now the spotlighted topic in the Android dev world and there is no doubt it is going to change the way we build UI on Android sooner or later.

With a single touch to change the way we define local variables in functions, we can now make it a “state” and embrace the auto-managed UI update (recomposition) bring to us by the compose internal.

But while we enjoying the MAGIC brought to us by Compose, have we ever wonder why do we even have to write in this exact syntax? Shall the code work as usual if we drop any of the component?

Let’s take a look at a simple Composable function (or say, a screen).

Here we have a button that displays the current value of a and it increments a when it is clicked. Magically, when we run it and click the button, you’ll see the value of a is preserved and incremented every time

Now let’s see what would happen if we declare a the same way we used to do for local variables.

If we run this and press the button, you would see nothing change with the UI. So why? Since a is now just a local variable, changing it doesn’t cause composable to recompose, and as we know that composable functions emit changes of UI, these change to the in-memory a is never emitted to the UI.

This is where mutableStateOf comes into play.

This function returns a SnapShotState that preserve the value by the internal Snapshot System and would trigger composable recomposition if a change occur to the managed state. We can rewrite our sample screen as this.

By making a a state, the place where we read the value of a is now essentially subscribing to the change of a (just like observing a LiveData), in this case, the Button‘s content composable (the curly bracket that wraps the text, aka the lambda we pass to button) would be recompose every time the value of a changes.

Hmmm 🤔, now you might be thinking that everything seems to work perfectly at this stage, so…. why do we even bother to remember?? Doesn’t declaring it as state already remembering it? (Finally get to the topic of this post)

Well, let’s try to break our sample screen again.

See the difference? Now we have another Text to display the value outside of the button. If we run this and press the button, you would see that the both of the text never changes. Why does this happen??

If you take a closer look at my statement few paragraphs ago

By making a a state, the place where we read the value of a is now essentially subscribing to the change of a (just like observing a LiveData), in this case, the Button‘s content composable (the curly bracket that wraps the text, aka the lambda we pass to button) would be recompose every time the value of a changes.

and apply to the case of composableScreen4 , since we have another Text composable that is reading a the wrapping composable, which is the SampleScreen*, is now subscribing to a, so every time the we press the button (= change value), the SampleScreen would get recomposed (= re-executed)

And in this case, every time we execute the SampleScreen, we are creating a new state object with initial value of 0, and resubscribing the child composables to the newly created state! This is why even though the recomposition occurs on user interaction with the use of state, we can never see the updated value being rendered.

Note: Column is an inline @Composable so it is not the one observing the state

Well guess how to solve this issue, remember !

The remember function would cache block we pass to it into a slot table, with a Don't invalid flag and the calculation block. The underlying operation would check the table to see whether it should

  • Cached the value if it is yet presented in the table
  • Redo the calculation and update the table if invalid flag is set to true

and finally return the resulting value of the calculation block.

When SampleScreen get executed and come across the line where we declare a , we now check the slot table to see whether the value is previously cached or not.

  • At the first time, it is yet cached so the state creation would be performed and cached inside the table
  • When recomposition, it sees the cached (and updated) state inside the table so just return the latest value

With both remember and mutableStateOf , our sample screen could function normally just as the very first example!!

After all of this attempt, we kinda have a clearer idea of what compose is doing behind the screen (get it? behind the SCREEN😉), but…. do we really have to know this in order to write in Compose UI? The answer is indeed “NOT REALLY”, as compose is built simple-and-easy-to-use by a fascinating team. However, knowing how it works under the hood may come into help when trying to debug in certain situation.

Now forget about these for now and happy COMPOSE your UI 👊

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Po C.
Po C.

Written by Po C.

Android Dev @ LINE Fukuoka / Android Dev 🇹🇼🇦🇺🇯🇵

No responses yet

Write a response