wiktor
09/23/2022, 8:19 PMFlow<ViewState>
)wiktor
09/23/2022, 8:19 PMclass BacklogComponent(
private val componentContext: ComponentContext,
private val storeFactory: StoreFactory,
) : Backlog, ComponentContext by componentContext {
private val backlogStore =
instanceKeeper.getStore { BacklogStoreFactory(storeFactory).create() }
override val models: Flow<BacklogViewState> = backlogStore.states
override fun onEvent(event: BacklogEvent) {
backlogStore.accept(event)
}
}
wiktor
09/23/2022, 8:19 PMinternal class BacklogStoreFactory(private val storeFactory: StoreFactory) {
private val items = mutableListOf<String>()
fun create(): BacklogStore =
object : BacklogStore,
Store<BacklogEvent, BacklogViewState, Nothing> by storeFactory.create(
name = "ListStore",
initialState = BacklogViewState(items),
bootstrapper = SimpleBootstrapper(Unit),
executorFactory = ::ExecutorImpl,
reducer = ReducerImpl,
) {}
private sealed interface Result {
data class Updated(val items: List<String>) : Result
}
private inner class ExecutorImpl :
CoroutineExecutor<BacklogEvent, Unit, BacklogViewState, Result, Nothing>() {
override fun executeIntent(
intent: BacklogEvent,
getState: () -> BacklogViewState
) {
when (intent) {
is BacklogEvent.AddItem -> {
items.add(intent.item)
dispatch(Result.Updated(items))
}
}
}
}
private object ReducerImpl : Reducer<BacklogViewState, Result> {
override fun BacklogViewState.reduce(result: Result): BacklogViewState =
when (result) {
is Result.Updated -> copy(items = result.items)
}
}
}
wiktor
09/23/2022, 8:20 PMclass MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val root = todoRoot(defaultComponentContext())
setContent {
ComposeAppTheme {
Surface(color = MaterialTheme.colors.background) {
RootContent(root)
}
}
}
}
private fun todoRoot(componentContext: ComponentContext): MainNavigation =
MainNavigationComponent(
componentContext = componentContext,
storeFactory = DefaultStoreFactory(),
)
}
@Composable
fun RootContent(component: MainNavigation) {
Children(stack = component.childStack) {
when (val child = it.instance) {
is MainNavigation.Child.BacklogChild -> BacklogContent(child.component)
}
}
}
@Composable
fun BacklogContent(component: Backlog) {
val model by component.models.collectAsState(BacklogViewState(emptyList()))
Items(model, component::onEvent)
}
@Composable
private fun Items(
model: BacklogViewState,
onEvent: (BacklogEvent) -> Unit
) {
Column {
var clickNumber by remember { mutableStateOf(1) }
Box(
Modifier.fillMaxWidth().fillMaxHeight()
.clickable {
onEvent(BacklogEvent.AddItem("Click $clickNumber"))
clickNumber += 1
}) {
val listState = rememberLazyListState()
LazyColumn(state = listState) {
items(model.items) {
Item(
item = it,
)
Divider()
}
}
}
}
}
wiktor
09/23/2022, 8:21 PMArkadii Ivanov
09/23/2022, 8:32 PMstate.copy(items = state.items + msg.item)
.wiktor
09/24/2022, 8:20 AMArkadii Ivanov
09/24/2022, 8:20 AMwiktor
09/24/2022, 8:21 AMwiktor
09/24/2022, 8:21 AMArkadii Ivanov
09/24/2022, 8:22 AMwiktor
09/24/2022, 8:25 AMwiktor
09/24/2022, 8:26 AMcollectAsState
?
val model by component.models.collectAsState(BacklogViewState(emptyList()))
Arkadii Ivanov
09/24/2022, 8:34 AMwiktor
09/24/2022, 8:35 AMArkadii Ivanov
09/24/2022, 8:41 AMwiktor
09/29/2022, 1:38 PMValue
instead nothing is rendered:
this works fine:Arkadii Ivanov
09/29/2022, 1:40 PMwiktor
09/29/2022, 1:40 PMimport com.arkivanov.decompose.value.Value
import com.arkivanov.mvikotlin.core.store.Store
import com.arkivanov.mvikotlin.rx.Disposable
typealias ValueObserver<T> = (T) -> Unit
fun <T : Any> Store<*, T, *>.asValue(): Value<T> =
object : Value<T>() {
override val value: T get() = state
private var disposables = emptyMap<ValueObserver<T>, Disposable>()
override fun subscribe(observer: ValueObserver<T>) {
val disposable = states(com.arkivanov.mvikotlin.rx.observer(onNext = observer))
this.disposables += observer to disposable
}
override fun unsubscribe(observer: ValueObserver<T>) {
val disposable = disposables[observer] ?: return
this.disposables -= observer
disposable.dispose()
}
}
Arkadii Ivanov
09/29/2022, 1:48 PMoverride fun subscribe(observer: ValueObserver<T>) {
val disposable = states(com.arkivanov.mvikotlin.rx.observer {
println(it)
observer(it)
})
this.disposables += observer to disposable
}
2. The Composable recomposes - add println
in BackologContent , right before Items
call.
I would need a reproducer to check what's wrong.Arkadii Ivanov
09/29/2022, 1:51 PMequal
to the previous state in order for the composable function to recompose. So it must be a different instance and with different data.wiktor
09/29/2022, 1:55 PMAlso the new state must not beeach time it’s a new data class obtained withto the previous state in order for the composable function to recompose. So it must be a different instance and with different data.equal
copy
(data class contains only list of strings)wiktor
09/29/2022, 1:56 PMArkadii Ivanov
09/29/2022, 2:03 PMby
operator references a wrong thing (wrong import?). Also worth checking inside observeAsState. You could cope the method to your project and add logs there as well.wiktor
09/29/2022, 2:04 PMpackage com.arkivanov.decompose.extensions.compose.jetpack
@androidx.compose.runtime.Composable public fun <T : kotlin.Any> com.arkivanov.decompose.value.Value<T>.subscribeAsState(): androidx.compose.runtime.State<T> { /* compiled code */ }
wiktor
09/29/2022, 2:05 PMwiktor
09/29/2022, 2:07 PMArkadii Ivanov
09/29/2022, 2:09 PMstate.value = it
.Arkadii Ivanov
09/29/2022, 2:09 PMby
operator? Wondering what it leads to.wiktor
09/29/2022, 2:11 PMwiktor
09/29/2022, 2:11 PMwiktor
09/29/2022, 2:12 PMby
only using =
and then .value
, with same resultArkadii Ivanov
09/29/2022, 2:12 PMwiktor
09/29/2022, 2:13 PMwiktor
09/29/2022, 2:24 PMtoList
that calls ArrayList(collection)
, it works fine with flow, but not with value,wiktor
09/29/2022, 2:25 PMlistOf
it starts to workArkadii Ivanov
09/29/2022, 2:29 PMwiktor
09/29/2022, 2:31 PMwiktor
09/29/2022, 2:31 PMtoList