https://kotlinlang.org logo
Title
r

rsktash

01/27/2022, 9:29 PM
[Solved] Hi I’ve encountered an issue with nested root components. Seems to be
unregister
is not being called when navigated back from inner router The error occurs when 1. I navigate to inner router component A. 2. Navigate back 3. and again navigate to router component A Root Router -> Inner Router -> Child -> NavBack -> Navigate forward
I’m sorry. It was originating from a bug in extended component context
Can we add a Factory for extended component context to simplify and minimize this kind of errors?
a

Arkadii Ivanov

01/27/2022, 10:10 PM
Could you suggest an example of such a factory?
t

trashcoder

03/10/2022, 10:04 PM
hello, i am running into the same error when updating from
decompose
0.3.1 to a newer version (i tried with 0.5.1, 0.5.0 and 0.4.0). the code does compile (i only had to update the imports for the
router
package) but i do get an error during runtime.
what i do:
i have 3
Configurations
with 3
Childs
. some
Childs
use the same components, so i thought it would be nice, if the components would only be created once, so that their state is shared between `Configurations`/`Childs`.
how i implemented it (works with
decompose
0.3.1):
so instead of creating a component in the
createChild()
function (which is provided as
childFactory
) and using its
ComponentContext
, i created variables in the root component (like the one for the
routerState
) and used the
ComponentContext.childContext()
function.
class RootComponent internal constructor(
    componentContext: ComponentContext,
    private val hue: (ComponentContext, (Hue.Output) -> Unit) -> Hue,
    private val homematicIP: (ComponentContext, (HomematicIP.Output) -> Unit) -> HomematicIP
) : Root, ComponentContext by componentContext {

    constructor(
        ...
    )

    override val hueComponent: Hue
// the following line fails
        get() = hue(childContext("hueComponent"), this::onHueOutput) 

    override val homematicIPComponent: HomematicIP
        get() = homematicIP(childContext("homematicIPComponent"), this::onHomematicIPOutput)

    private val router =
        router<Configuration, Root.Child>(
            initialConfiguration = Configuration.Dashboard,
            handleBackButton = true,
            childFactory = ::createChild
        )

    override val routerState: Value<RouterState<*, Root.Child>> = router.state

    private fun createChild(configuration: Configuration, componentContext: ComponentContext): Root.Child =
        when (configuration) {
            is Configuration.Dashboard -> Root.Child.Dashboard(hueComponent, homematicIPComponent)
            is Configuration.Hue -> Root.Child.Hue(hueComponent)
            is Configuration.HomematicIP -> Root.Child.HomematicIP(homematicIPComponent)
        }

    ...
}
interface Root {
    val hueComponent: Hue
    val homematicIPComponent: HomematicIP
    val routerState: Value<RouterState<*, Child>>

    sealed class Child {
        data class Dashboard(
            val hueComponent: com.trashcoder.trashboard.common.philips_hue.integration.Hue,
            val homematicIPComponent: com.trashcoder.trashboard.common.homematic_ip.integration.HomematicIP) : Child()
        data class Hue(
            val hueComponent: com.trashcoder.trashboard.common.philips_hue.integration.Hue) : Child()
        data class HomematicIP(
            val homematicIPComponent: com.trashcoder.trashboard.common.homematic_ip.integration.HomematicIP) : Child()
    }

    ...
}
Exception in thread "main" java.lang.IllegalStateException: Check failed.
	at com.arkivanov.essenty.statekeeper.DefaultStateKeeperDispatcher.register(DefaultStateKeeperDispatcher.kt:42)
	at com.arkivanov.decompose.statekeeper.ChildStateKeeperKt.child(ChildStateKeeper.kt:11)
	at com.arkivanov.decompose.ComponentContextExtKt.childContext(ComponentContextExt.kt:21)
	at com.arkivanov.decompose.ComponentContextExtKt.childContext$default(ComponentContextExt.kt:18)
	at com.trashcoder.trashboard.common.root.integration.RootComponent.getHueComponent(RootComponent.kt:50)
a

Arkadii Ivanov

03/10/2022, 10:34 PM
@trashcoder You are creating multiple instances of the same child components with the same keys. E.g. your
hueComponent
getter calls
childContext("hueComponent")
every time when it is called. The second attempt of creating child context with the same key will fail. One of the possible fixes is to use
lazy
instead:
override val hueComponent: Hue by lazy { hue(childContext("hueComponent"), this::onHueOutput) }
Or maybe you can just assign the value (depends on your needs)
override val hueComponent: Hue = hue(childContext("hueComponent"), this::onHueOutput)
t

trashcoder

03/10/2022, 10:59 PM
Wow. That was fast. I will try it tomorrow. Thanks a lot! 🙏🏻
👍 1
It works! I cannot believe that i really thought that the term after "get() =" is only for initialization... this is so embarrassing 🙈 😅 (first kotlin project) btw: now that i have unique components (instead of creating and booting them each time 🤦), i had to change my router calls (
push
and
replaceCurrent
) to
bringToFront
to ensure unique
Configurations
... Thanks again! :kotlin-intensifies:
🎉 1