Skip to main content

TreeProps

caution

This page covers the old Java Spec API. If the Spec API is not being used, refer to the TreeProps section of the 'Types of Props' page.

A TreeProp is a special type of prop that is transparently passed from a parent component to its children. It provides a convenient way to share contextual data or utilities in a tree without having to explicitly pass props to every component in a hierarchy.

A good example of a TreeProp is a 'prefetcher', which fetches network images ahead of render time. Since images are commonplace, the prefetcher is widely used. The prefetcher implementation might be defined for any component that needs to use it, without having to pass it as a prop in the entire component hierarchy.

Declaring a TreeProp​

In order to declare a TreeProp, use TreePropProvider:

class ParentKComponent(private val prefetcher: Prefetcher, private val imageUri: Uri) :
KComponent() {

override fun ComponentScope.render(): Component? {
return TreePropProvider(Prefetcher::class.java to prefetcher) { ChildKComponent(imageUri) }
}
}
note

Only one TreeProp can be declared for any one given type. If a child of ParentComponent also defines a TreeProp of type Prefetcher, it will override the value of that TreeProp for all its children (but not for itself).

Using a TreeProp​

The child component can access the TreeProp value through a ComponentScope.getTreeProp<Type>() method call, where Type is the same type that was provided in the TreePropProvider call in one of its parents.

class ChildKComponent(private val imageUri: Uri) : KComponent() {

override fun ComponentScope.render(): Component {
val prefetcher = getTreeProp<Prefetcher>()

prefetcher?.prefetch(imageUri)
// ...
return Text("Prefetch image when this component is created")
}
}
IMPORTANT

Once created, the TreeProp value is passed down to all children but isn't accessible from the component that created the TreeProp.

To access a TreeProp from the component that created it, transform it into State as follows:

class ParentComponentTreePropAsStateKComponent() : KComponent() {

override fun ComponentScope.render(): Component? {
val importantHelper = useState { ImportantHelper() }

return TreePropProvider(ImportantHelper::class.java to importantHelper) {
Column() { child(Text(text = "ImportantHelper can be used as State in render function")) }
}
}
}

TreeProps with Lists​

TreeProps can be used in components, sections and lazy collections. They can even be modified between them.

The following code is an example of a logging data structure that is passed down from the root component to capture information about the hierarchy.

public class LogContext {
public final String tag;

public LogContext(String tag) {
this.tag = tag;
}

public static LogContext append(@Nullable LogContext parentContext, String tag) {
if (parentContext == null) {
return new LogContext(tag);
}
return new LogContext(parentContext.tag + ":" + tag);
}

public String toString() {
return tag;
}
}
tip

Immutable TreeProps are easier to understand so try to follow that design pattern whenever possible.

The following diagram shows the component hierarchy.

Start by setting up the RootComponent with the LazyList as one of its children:

class RootKComponent : KComponent() {

override fun ComponentScope.render(): Component? {
return TreePropProvider(LogContext::class.java to LogContext("root")) {
Column() {
child(LeafKComponent())
child(LazyList(style = Style.height(500.dp)) { child(TopGroupKComponent()) })
}
}
}
}

The TopGroupKComponent takes in the root TreeProp and adds its "top" tag to it:

class TopGroupKComponent : KComponent() {

override fun ComponentScope.render(): Component? {
val parentLogContext = getTreeProp<LogContext>()
val topGroupLogContext = LogContext.append(parentLogContext, "top")

return TreePropProvider(LogContext::class.java to topGroupLogContext) {
Column {
child(LeafKComponent())
child(BottomGroupKComponent())
}
}
}
}

The bottom part here has been omitted for brevity, but it can be found in the repository under samples.

The leaf node renders the TreeProp as text in the example case but would normally perform some sort of logging based on the context:

class LeafKComponent() : KComponent() {

override fun ComponentScope.render(): Component {
val parentLogContext = getTreeProp<LogContext>()
return Text(text = LogContext.append(parentLogContext, "leaf").toString())
}
}

The on-screen result is three rows of text that read:

  • "root:leaf"
  • "root:top:leaf"
  • "root:top:bottom:leaf"

This illustrates how TreeProps propagate through both component and section trees and can be used to selectively share information with their children.