Hi! I’m trying to figure out what is the proper wa...
# compose-desktop
y
Hi! I’m trying to figure out what is the proper way to update observables from the outside of the composition? For example, I have this primitive state holder:
Copy code
class MyState(initialValue: String) {
    var value by mutableStateOf(initialValue)
}
If I create an instance of
MyState
at some point within the composition and immediately try to update
myState.value
from a new coroutine with
Dispatchers.Default
, I will get the following exception:
Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied
. Usage of
Dispatchers.Main
seems to help. Each composable is both a separate snapshot and a coroutine in
Dispatchers.Main
? So, scheduling a coroutine on top of
Main
is always safe, as the current composition will be committed by the moment the observable value is updated from the launched coroutine. Is the assumption with
Dispatchers.Main
true?
g
This is usually because your coroutine executed before the composition even created the state. you can use LaunchedEffect to solve this because it will tie the coroutines lifecycle to the lifecycle of the composition for that scope. You should also always be using a coroutine scope that came from Compose, you get this with rememberCoroutineScope() Also, why do you need to wrap the state value in a class? The MutableState class already does this for you as it has it's own getter/setter methods, or exposes the .value to you.
A safe rule of thumb is to always assume that a coroutine is going to execute absolutely instantly (and before stuff you wouldn't expect) unless you force it to otherwise.
y
Also, why do you need to wrap the state value in a class?
MyState
class has several
mutableStateOf()
fields. The problem is that it updates them from external events. During the class initialization, it subscribes for some external events and updates observable properties as these events arrive. The events are dispatched from a separate background thread. Thus, they may arrive as soon as an instance is initialized, knowing nothing about the nature of Compose observables.
you can use LaunchedEffect to solve this
In this case, it is just inconvenient. I could ask for
scope
in the class constructor, stating in docs that the scope from Compose should be passed here. But this is something I would like to avoid, if possible. But as for now, I don’t see other workable options besides
Dispatchers.Main
(on which I should not rely).
g
the main thread doesn't always guarantee that you'll be safe. States are intrinsically linked to compose, so you need to always generate and manage them from compose, not from a separate data object or you're going to run into race conditions like that one
👍 1
y
Sad but true. @Garret Yoder thank you.