Hi people! Are there any ways to create components...
# compose
j
Hi people! Are there any ways to create components in compose like how we can create a react component? I am not an expert in React, neither in Compose, but following the React Kotlin tutorial and hooking it up with Kodein I think it would be cool to have a component in Compose as well. I think having lifecycle callbacks, dependency injection and component scoped viewmodels etc. would be cool. The biggest example I could find of a compose app is the jet news app (https://github.com/android/compose-samples) but there the dependencies are just passed through the composable functions as arguments. So my question is: Should I look into this direction of having composable components? Is this where compose wants to go? Especially if an app will basically be 1 activity without any fragments and only composable functions I can imagine dependency injection might be wanted 🙂 I was messing around to make my own compose component. This is for example a component from the Kotlin react tutorial:
Copy code
class App : RComponent<RProps, RState>() {
  override fun RBuilder.render() {
        // typesafe HTML goes here!
    }
}
Usually this is build using a static helper method to add a child to the parent component. In spirit of this, I did manage to put a component class into a state and call a render method upon it. Also hooking it up to the lifecycle of the parent:
Copy code
private class SomeButtonComponent(private val props: SomeButtonProps) :
    BaseComponent<SomeButtonProps>(props) {

    override fun DI.MainBuilder.initModule() {
        // Nothing to do here
    }

    @Composable
    override fun render() {
        Button(onClick = {
            lifecycleScope.launchWhenCreated {
                props.handler()
                delay(200)
            }
        }) {
            Text(text = "I am a button")
        }
    }

    override fun cleanUp() {
        // Nothing to do here
    }

    companion object : BaseComponentRenderer<SomeButtonComponent, SomeButtonProps>() {

        override fun provideComponent(props: SomeButtonProps): SomeButtonComponent {
            return SomeButtonComponent(props)
        }
    }
}

data class SomeButtonProps(
    override val parent: ComposableComponent, // Contains Kodein DI and is a lifecycle owner
    val handler: () -> Unit // Callback method after button click
) : BaseProps
This is similar as the
render
method is called just like the react component. Also the lifecycleScope hooks into its parents/the activity's lifecycle scope (and it gets killed in onDispose). You can call it like this:
Copy code
setContent {
            SomeButtonComponent.render(SomeButtonProps(this@MainActivity) {
                viewModel.onLoggedIn()
            })
        }
I couldn't really find anything on how I could create 1 component and keep its instance, but it could have been that I was looking in the wrong place. In React you create a child and then put it in the graph. Therefore I tried to copy the kotlin react example and make it more compose-y. This will call in the same way a static function, which under the hood creates a child. If I am correct, the
state {...}
is only called once (if it doesn't change of course) so creating the component there, do some injection etc. should be fine in the State initialization block. So this code here basically creates my composable component and then renders it and nudges it to destroy itself once it can be disposed of
Copy code
abstract class BaseComponentRenderer<C : BaseComponent<P>, P : BaseProps> {

    protected abstract fun provideComponent(props: P): C

    @Composable
    fun render(props: P) {
        state {
            provideComponent(props)
        }.value.also {
            it.render()
            onDispose {
                it.destroy()
            }
        }
    }

}