Hello! Currently there is an issue in the `Router`...
# decompose
a
Hello! Currently there is an issue in the
Router
API - the
childFactory
callback is called during the
Router
initialization, and it's not possible to obtain the `Router`'s reference safely from the callback. E.g. consider the following snippet:
Copy code
private val router = 
    router(initialConfiguration = Config.Main) {
        // Can't use router here
    }
Also when using separate function for the child factory, there is a foot gun:
Copy code
private val router = router(initialConfiguration = Config.Main, childFactory = ::child)

private fun child(...): Child {
    // The router property may be null at this point
    DetailsComponent(onFinished = router::pop) // This may crash with NPE when onFinished is called
    DetailsComponent(onFinished = { router.pop() }) // This is OK
}
I have been working on the new API for a couple of weeks, there are multiple ideas with various pros and cons. The API on the screenshot is what I liked the most. The idea is to introduce separate
ChildNavigator
entity which is just a relay of navigation events. The
childStack
function subscribes to
ChildNavigator
events and handles all the routing job, and returns
Value<ChildStack>
.
ChildStack
is essentially the same thing as
RouterState
currently. This way we have the reference to
ChildNavigator
beforehand. But there is no such a single entity as
Router
that could
navigate
and provide
RouterState
at the same time. What do you think?
👀 2
a
so the core issue is the circular references between the router init and child factory
and by separating
stack
and
navigation
we get around it?
so
navigation
is just a mutator of the
stack
obj then
if i've got this right, then I think it's a pretty nice solution to the problem
a
Almost correct! In that snippet
StackNavigation
is just a simple relay. It's like observable and consumer at the same time. The
childStack
function receives
StackNavigation
and subscribes to its events. Then when you call
navigation.navigate {}
(or
push
,
pop
, etc), its immediately emits an
Event
with the
transformer
and
onComplete
supplied). The internal logic of
childStack
receives the event does all the job. And updates
Value<ChildStack>
. There is already an open PR - https://github.com/arkivanov/Decompose/pull/140
a
If I understand that correctly, it's the method reference causing this error because if memory serves me the method reference requires that property to be initialized? I find the
StackNavigation
to be an interesting api, although if this is just to solve for the method reference issue I personally would prefer to just use the lambda reference to avoid separating out the the two in the router for this edge case. Although if there is a desire to use method reference, then this API seems like a reasonable change to enable that.
a
The method referencing is just one of the use cases. Another use case is when you implement childFactory as a lambda, not as a separate private function. In this case you can't use the property, there is a compile error. You may also want to pass the Router instance to a child component, which is not recommended but still. This may crash with NPE again.
👍 1
a
Oh okay, I could see the necessity for separating them out from one another then for these use cases. Would that mean that the other router api would go away, or is this new approach just a second way to handle these specific use cases?
a
I have been receiving only positive feedback so far, here and in another chat. Also, please mind https://github.com/arkivanov/Decompose/issues/134 So I guess the current Router will go away eventually, via the deprecation cycle.
👍 2