One Off the Slack: Using State Holders

Jean Tuffier started off quoting a bit from the Using Jetpack Libraries in Compose presentation from Google I|O 2021:

As a rule of thumb, you should create a view model to hold your state […] when the composable is close to the root of the screen. When that’s not the case, for examples, composables that are reusables […], don’t use a view model , instead create a regular state holder class to manage state and expose events.

Jean then went on:

I couldn’t find any related code in the compose-sample project. Does anyone hasve a concrete/sharable example of such a “regular state holder class”?

Albert Chang offered:

If I understood it correctly results of all remember*State() functions are state holder classes, e.g. ``ScaffoldState, LazyListState`, etc.

Jean remained unconvinced:

It’s a bit problematic for me since that reusable composable used to get data from an api and/or db though it’s view model 😕 (Imagine a custom row composable displaying different content in each instances) How would you approach that?

Albert then linked to a Slack entry from a month prior from Google’s Jim Sproch:

Best practice is probably to avoid referencing AAC ViewModels in your composable functions. They are not platform-agnostic and almost always imply a strong coupling between your composable function and the rest of your application.

Instead, create an interface and only refer to the interface from your composable (this allows you to easily create an alternative implementation of the widget data that does not use AAC ViewModels), or read from the view model up at the top of your application and split it off into only the non-ViewModel data that your individual composables need.

When your composable does not behave well in Preview, that is almost always an indication your composable is not sufficiently isolated from the rest of the platform/application code.

Google’s Sean McQuillan echoed the sentiment:

The model to think here is that the idea case composable doesn’t get data it’s given data via params.

Anything that fetches its own data is inherently stateful. When possible it’s better to provide a stateless ui display composable and handle state above it

In practice there’s a dozen ways to slice this API, but the simplest is to use state hoisting of (param: T, onParamChange: (T) -> Unit)

Jean then identified the source of the disconnect:

My current logic is based on a fragment that was inserted multiple time inside a parent fragment and each instance were in charge of getting their own data. I guess the approach is inherently different with compose, where the opposite is encouraged 🙂

Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!