Olivier Patry
04/15/2021, 10:24 PM@Composable
fun GameScreen(rules: GameRules) {
Column {
LimitStatus(rules)
var board by remember { mutableStateOf(rules.board) }
rules.addObserver { _, arg ->
if (arg == null) {
board = rules.board
}
}
GameBoard(board)
}
}
Idea would be to update the board
when observable ask me to do so (null args
here means board update…).
I'm not sure I'm 100% aware of the recomposition rules, I would have expected an update here but it doesn't happen (despite my observer is properly called).
I guess, since the Java object ref doesn't change (board instance if the same, always coming from the rules object), nothing changes from Compose perspective.
Any idea?DisposableEffect
to add/remove observer I guess, otherwise I'll have too many of them at some point, but for now, it's never recomposed so…)Zach Klippenstein (he/him) [MOD]
04/15/2021, 11:35 PMFlow.collectAsState
to write yourself a generic extensionuli
04/16/2021, 6:55 AMOlivier Patry
04/16/2021, 8:06 AMcollectAsState
calls produceState
which itself is a remember { mutableStateOf(x) }
calling an effect to implement the observe logic.
Did I understand correctly?
Here is my updated impl (still not working)
@Composable
inline fun <reified T> GameRules.observeAsState(initialValue: T): State<T> {
val state = remember { mutableStateOf(initialValue) }
DisposableEffect(this) {
val observer = Observer { observable, arg ->
val rules = observable as GameRules
when {
initialValue is Board && arg == null -> {
state.value = rules.board.copy() as T
}
initialValue is GameRules.Limit && arg is GameRules.Limit -> {
state.value = rules.limit as T
}
initialValue is GameRules.GameStatus && arg is GameRules.GameStatus -> {
state.value = rules.status as T
}
}
}
addObserver(observer)
onDispose {
deleteObserver(observer)
}
}
return state
}
and used as
val board by rules.observeAsState(rules.board)
copy()
doesn't seem to help, my problem might come from elsewhereuli
04/16/2021, 8:19 AMDisposableEffect
to add/remove observer?Olivier Patry
04/16/2021, 8:52 AMstate.value =
is called when it should (following Observer.update
)Observable
GameRules
in a GameRuleState
exposing properties as mutableStateOf()
.
It doesn't change, so I guess my problem comes from something else than state management 🤔, what do you think?
class GameRulesState(private val gameRules: GameRules) {
val board = mutableStateOf(gameRules.board)
val limit = mutableStateOf(gameRules.limit)
val status = mutableStateOf(gameRules.status)
private val onUpdate = Observer { observable, arg ->
val rules = observable as GameRules
when (arg) {
null -> {
board.value = rules.board.copy()
}
is GameRules.Limit -> {
limit.value = rules.limit
}
is GameRules.GameStatus -> {
status.value = rules.status
}
}
}
init {
gameRules.addObserver(onUpdate)
}
}
@Composable
fun GameScreen(rules: GameRulesState) {
Column {
LimitStatus(rules)
// val board by rules.observeAsState(rules.board)
val board = remember { rules.board }
uli
04/16/2021, 9:32 AM@Composable
fun <T : R, R> Flow<T>.collectAsState(
initial: R,
context: CoroutineContext = EmptyCoroutineContext
): State<R> = produceState(initial, this, context) {
if (context == EmptyCoroutineContext) {
collect { value = it }
} else withContext(context) {
collect { value = it }
}
}
No remember
but produceState
.
collect {...}
would become your val observer = Observer {…}
Olivier Patry
04/16/2021, 10:04 AMproduceState
impl is
@Composable
fun <T> produceState(
initialValue: T,
key1: Any?,
key2: Any?,
@BuilderInference producer: suspend ProduceStateScope<T>.() -> Unit
): State<T> {
val result = remember { mutableStateOf(initialValue) }
LaunchedEffect(key1, key2) {
ProduceStateScopeImpl(result, coroutineContext).producer()
}
return result
}
So, I understand this as being similar to the remember call 🤔Observable
(primitive types in particular).
What remains not recomposed is my "complex" board type
data class Board(val lineCount: Int, val columnCount: Int) {
val game: Array<Array<Tile>>
...
}
So, I'll dig in that directioncopy()
so GameRules.board
being always the same object, modified "in place", the state value comparison always returned true (so nothing to do)
2. even using copy()
it wasn't working since my Board
contained a "complex object" initialization (Array
of Array
) that wasn't deep copied.
So, I implemented a custom copy()
implementation to deep copy the Board
data and now it works.
It wasn't really linked to recomposition at the end 🙂uli
04/16/2021, 6:27 PM