jwstegemann / fritz2

Easily build reactive web-apps in Kotlin based on flows and coroutines.
https://www.fritz2.dev
MIT License
653 stars 28 forks source link

Fix reactively nested beforeUnmount Calls #793

Closed metin-kale closed 1 year ago

metin-kale commented 1 year ago

The lifecycle function beforeUnmount did not work properly in some reactively nested scenarios.

In a simple example with one Store and a corresponding Store<*>.render it is working without problems, but when nesting render-blocks, it is only called for the outer MountPoint.

Example:

val outerStore = storeOf(Id.next())
val innerStore = storeOf(Id.next())

input {
    type("Button")
    value("Update Outer Store")
}.clicks.map { Id.next() } handledBy outerStore.update
input {
    type("Button")
    value("Update Inner Store")
}.clicks.map { Id.next() } handledBy innerStore.update

outerStore.data.render {
    p { +it }
    afterMount { _, _ -> console.log("outer afterMount") }
    beforeUnmount { _, _ -> console.log("outer beforeUnmount") }

    innerStore.data.render {
        p { +it }
        afterMount { _, _ -> console.log("inner afterMount") }
        beforeUnmount { _, _ -> console.log("inner beforeUnmount") } // this gets not called, if `outerStore` changes
    }
}

If one clicks the first button (Update Outer Store) one will get the following logs:

    outer beforeUnmount
    inner afterMount
    outer afterMount

afterMount is called for both, but beforeUnmount is only called for the outer MountPoint. This was a bug, as beforeUnmount must be called recursively on all levels beneath some changing MountPoint!

This PR fixes the issue so it should now work properly in all nesting scenarios.

As PoC also some internal workaround in the portalling-engine has now been refactored towards a more idiomatic approach using beforeUnmount.

fixes #782