One Off the Slack: The Scope of State

Dominic Fisher asked:

Is there much difference between SnapshotStateList<T> and MutableState<List<T>> ? In particular, is there any difference in the granularity of how mutations trigger recomposition? (Like in a LazyColumnFor)

Google’s Adam Powell and Leland Richardson had differing takes on that… and this time, I’m on Leland’s side.

(sorry, Adam!)

Adam’s initial reaction was:

The former is basically a fancy version of the latter, but that uses the kotlinx immutable collections and some other optimizations under the hood for efficiency

Leland countered:

I dunno if I would agree with SnapshotStateList<T> as a fancy version of MutableState<List<T>>.

When you’re using the latter, you’re essentially treating the list itself as an immutable piece of data, and whenver you want to change it, you have to provide a whole new list. So for example, to add an item in the middle of the list:

state.value =  
  state.value.let { 
    it.subList(0, n) + item + it.subList(n)

This is because the “tracking” is really done on a single value, not on the individual elements of the list. You have a “tracked” reference to a list, and you’re updating what that reference points to.

On the other hand, the SnapshotStateList is made to look like a MutableList, but all mutations are tracked properly by Compose.

For instance, adding an element in the middle of the list is just:

state.insertAt(n, item)

In other words, with MutableState<T>, the T itself is the state, and we replace a T instance with another T instance to affect changes. With SnapshotStateList<T>, once again T is the state… but this time, it is a list of T objects, and we change individual elements in the list to affect changes. So, while both MutableState<List<T>> and SnapshotStateList<T> are states representing lists of T, with MutableState, we replace the whole list, and with SnapshotStateList, we mutate the contents of the list.

Leland went on to say:

This is really handy if you’re really thinking of the list and its elements as the stateful part of your app. However, I tend to lean away from SnapshotStateList in most cases just because I think the MutableState variant is not going to be any worse performance wise and it introduces fewer new concepts into your app (ie, you are probably already familiar using both MutableState and List all over the place, but likely not SnapshotStateList).

That said, it can be very useful in specific situations.

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