-
Notifications
You must be signed in to change notification settings - Fork 6
Home
What is component-tree
When you write code in compose-ui, in most cases, you pass a state as a composable function parameter and then the functions renders it. It starts from the root state <-> root composable pair, all the way down to the text and buttons state. The start of your composable code usually looks something like this:
// This scope holds a state, the one the composable function is about to render. This is non-composable world
val composeRootState = ComposeStateRootFactory.getState() // This state is hosted/scoped here
@Composable
AComposableScope(composeRootState: ComposeRootState) { // Joint point between Composable world and non-Composable world
ComposeRootRendererFunction(composeRootState) // This is a Composable function that knows how to render implementations of ComposeRootState
}
A root state holder could be an Activity in the case of Android or a Window in the case of a Desktop App or a UiViewController in the case of iOS. These are the entry points to the composable world in the different platforms.
Another way of using compose is to host the state internally in the compose Nodes, using remember
but the problem with this approach is that we lose testability and the ability to pass different implementations of the State to the composable function.
Back to hoisting the state outside the composable function(which is the practice this project focus on), very often your App state will be composed of sub state within itself. To visualize the point let's model it like bellow, let's assume we have a nested renderable state with 3 levels.
class ComposeStateRootImpl: IComposeStateRoot {
val composeStateLevel2 = ComposeStateLevel2()
}
class ComposeStateLevel2Impl : IComposeStateLevel2 {
val composeStateLevel3 = ComposeStateLevel3()
}
class ComposeStateLevel3Impl : IComposeStateLevel3
Typically to render that App state we will do something like:
@Composable
fun ComposeRootRendererFunction(stateRoot: IComposeStateRoot) {
...
ComposeLevel2RendererFunction(stateRoot.composeStateLevel2)
...
}
@Composable
fun ComposeLevel2RendererFunction(stateLevel2: ComposedStateLevel2) {
...
ComposeLevel3RendererFunction(stateLevel2.composeStateLevel3)
...
}
@Composable
fun ComposeLevel3RendererFunction(stateLevel3: ComposedStateLevel3) {
// do something with stateLevel3
}
In this case the state is being hosted in the parent composable state. All the way up to the Activity or the root composable's state. A more complete scene of the previous code shown at the beggining.
class MainActivity : ComponentActivity() {
// MainActivity host the state
val composeStateRoot = ComposeStateRootProvider.get()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// MainActivity hooks the state in to the Composable renderer function
setContent {
ComposeRootRendererFunction(composedStateRoot)
}
}
}
In this case, the Activity plays the role of the glue class that binds the 2 elements, the composable function and the state it renders. The same role the Activity is playing in this example, is the same role a Component does in component-tree. A Component is basically a class that host/hold a state for a composable function to pick up and render at some point.
class SomeComponent : Component {
val someComponentState: SomeComponentState()
//...
@Composable
abstract fun Content(modifier: Modifier) {
SomeComponentComposable(someComponentState)
}
//...
}
@Composable
fun SomeComponentComposable(someComponentState: SomeComponentState) {}
data class SomeComponentState
That is all it is, a glue class for 2 things, a Composable function and the State it renders.
Now, the major difference between the Activity and a Component, is that you have control of its constructor and internal implementation, things you don't have in the Android Activity per say, and it really makes a huge difference. This allows you to build Component trees or any other structure, control of dependency injection, control of Component serialization, control of your own back stack and so much.
Continue exploring the project and you will find out.